Implemented ConfuserEx deobfuscator
x86 cflow and x86 constant decryption Backport of LINQ (LinqBridge) Shift left/right emulation fixes in de4dot core Block class extended to hold additional information
This commit is contained in:
parent
126758fa6f
commit
23477ccb5f
Binary file not shown.
Binary file not shown.
|
@ -1,320 +1,348 @@
|
|||
/*
|
||||
Copyright (C) 2011-2015 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 dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public class Block : BaseBlock {
|
||||
List<Instr> instructions = new List<Instr>();
|
||||
|
||||
// List of all explicit (non-fall-through) targets. It's just one if it's a normal
|
||||
// branch, but if it's a switch, it could be many targets.
|
||||
List<Block> targets;
|
||||
|
||||
// This is the fall through Block (non branch instructions)
|
||||
Block fallThrough;
|
||||
|
||||
// All blocks that fall through or branches to this block
|
||||
List<Block> sources = new List<Block>();
|
||||
|
||||
public Block FallThrough {
|
||||
get { return fallThrough; }
|
||||
set { fallThrough = value; }
|
||||
}
|
||||
|
||||
public List<Block> Targets {
|
||||
get { return targets; }
|
||||
set { targets = value; }
|
||||
}
|
||||
|
||||
public List<Block> Sources {
|
||||
get { return sources; }
|
||||
}
|
||||
|
||||
public Instr FirstInstr {
|
||||
get {
|
||||
if (instructions.Count == 0)
|
||||
Add(new Instr(OpCodes.Nop.ToInstruction()));
|
||||
return instructions[0];
|
||||
}
|
||||
}
|
||||
|
||||
public Instr LastInstr {
|
||||
get {
|
||||
if (instructions.Count == 0)
|
||||
Add(new Instr(OpCodes.Nop.ToInstruction()));
|
||||
return instructions[instructions.Count - 1];
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(Instr instr) {
|
||||
instructions.Add(instr);
|
||||
}
|
||||
|
||||
public void Insert(int index, Instruction instr) {
|
||||
instructions.Insert(index, new Instr(instr));
|
||||
}
|
||||
|
||||
public List<Instr> Instructions {
|
||||
get { return instructions; }
|
||||
}
|
||||
|
||||
// If last instr is a br/br.s, removes it and replaces it with a fall through
|
||||
public void RemoveLastBr() {
|
||||
if (!LastInstr.IsBr())
|
||||
return;
|
||||
|
||||
if (fallThrough != null || (LastInstr.Operand != null && (targets == null || targets.Count != 1)))
|
||||
throw new ApplicationException("Invalid block state when last instr is a br/br.s");
|
||||
fallThrough = LastInstr.Operand != null ? targets[0] : null;
|
||||
targets = null;
|
||||
instructions.RemoveAt(instructions.Count - 1);
|
||||
}
|
||||
|
||||
public void Replace(int index, int num, Instruction instruction) {
|
||||
if (num <= 0)
|
||||
throw new ArgumentOutOfRangeException("num");
|
||||
Remove(index, num);
|
||||
instructions.Insert(index, new Instr(instruction));
|
||||
}
|
||||
|
||||
public void Remove(int index, int num) {
|
||||
if (index + num > instructions.Count)
|
||||
throw new ApplicationException("Overflow");
|
||||
if (num > 0 && index + num == instructions.Count && LastInstr.IsConditionalBranch())
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
instructions.RemoveRange(index, num);
|
||||
}
|
||||
|
||||
public void Remove(IEnumerable<int> indexes) {
|
||||
var instrsToDelete = new List<int>(Utils.Unique(indexes));
|
||||
instrsToDelete.Sort();
|
||||
instrsToDelete.Reverse();
|
||||
foreach (var index in instrsToDelete)
|
||||
Remove(index, 1);
|
||||
}
|
||||
|
||||
// Replace the last instructions with a branch to target
|
||||
public void ReplaceLastInstrsWithBranch(int numInstrs, Block target) {
|
||||
if (numInstrs < 0 || numInstrs > instructions.Count)
|
||||
throw new ApplicationException("Invalid numInstrs to replace with branch");
|
||||
if (target == null)
|
||||
throw new ApplicationException("Invalid new target, it's null");
|
||||
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
if (numInstrs > 0)
|
||||
instructions.RemoveRange(instructions.Count - numInstrs, numInstrs);
|
||||
fallThrough = target;
|
||||
target.sources.Add(this);
|
||||
}
|
||||
|
||||
public void ReplaceLastNonBranchWithBranch(int numInstrs, Block target) {
|
||||
if (LastInstr.IsBr())
|
||||
numInstrs++;
|
||||
ReplaceLastInstrsWithBranch(numInstrs, target);
|
||||
}
|
||||
|
||||
public void ReplaceBccWithBranch(bool isTaken) {
|
||||
Block target = isTaken ? targets[0] : fallThrough;
|
||||
ReplaceLastInstrsWithBranch(1, target);
|
||||
}
|
||||
|
||||
public void ReplaceSwitchWithBranch(Block target) {
|
||||
if (LastInstr.OpCode.Code != Code.Switch)
|
||||
throw new ApplicationException("Last instruction is not a switch");
|
||||
ReplaceLastInstrsWithBranch(1, target);
|
||||
}
|
||||
|
||||
public void SetNewFallThrough(Block newFallThrough) {
|
||||
DisconnectFromFallThrough();
|
||||
fallThrough = newFallThrough;
|
||||
newFallThrough.sources.Add(this);
|
||||
}
|
||||
|
||||
public void SetNewTarget(int index, Block newTarget) {
|
||||
DisconnectFromBlock(targets[index]);
|
||||
targets[index] = newTarget;
|
||||
newTarget.sources.Add(this);
|
||||
}
|
||||
|
||||
public void RemoveDeadBlock() {
|
||||
if (sources.Count != 0)
|
||||
throw new ApplicationException("Trying to remove a non-dead block");
|
||||
RemoveGuaranteedDeadBlock();
|
||||
}
|
||||
|
||||
// Removes a block that has been guaranteed to be dead. This method won't verify
|
||||
// that it really is dead.
|
||||
public void RemoveGuaranteedDeadBlock() {
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
Parent = null;
|
||||
}
|
||||
|
||||
void DisconnectFromFallThroughAndTargets() {
|
||||
DisconnectFromFallThrough();
|
||||
DisconnectFromTargets();
|
||||
}
|
||||
|
||||
void DisconnectFromFallThrough() {
|
||||
if (fallThrough != null) {
|
||||
DisconnectFromBlock(fallThrough);
|
||||
fallThrough = null;
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectFromTargets() {
|
||||
if (targets != null) {
|
||||
foreach (var target in targets)
|
||||
DisconnectFromBlock(target);
|
||||
targets = null;
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectFromBlock(Block target) {
|
||||
if (!target.sources.Remove(this))
|
||||
throw new ApplicationException("Could not remove the block from its target block");
|
||||
}
|
||||
|
||||
public int CountTargets() {
|
||||
int count = fallThrough != null ? 1 : 0;
|
||||
if (targets != null)
|
||||
count += targets.Count;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Returns the target iff it has only ONE target. Else it returns null.
|
||||
public Block GetOnlyTarget() {
|
||||
if (CountTargets() != 1)
|
||||
return null;
|
||||
if (fallThrough != null)
|
||||
return fallThrough;
|
||||
return targets[0];
|
||||
}
|
||||
|
||||
// Returns all targets. FallThrough (if not null) is always returned first!
|
||||
public IEnumerable<Block> GetTargets() {
|
||||
if (fallThrough != null)
|
||||
yield return fallThrough;
|
||||
if (targets != null) {
|
||||
foreach (var block in targets)
|
||||
yield return block;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true iff other is the only block in Sources
|
||||
public bool IsOnlySource(Block other) {
|
||||
return sources.Count == 1 && sources[0] == other;
|
||||
}
|
||||
|
||||
// Returns true if we can merge other with this
|
||||
public bool CanMerge(Block other) {
|
||||
return CanAppend(other) && other.IsOnlySource(this);
|
||||
}
|
||||
|
||||
// Merge two blocks into one
|
||||
public void Merge(Block other) {
|
||||
if (!CanMerge(other))
|
||||
throw new ApplicationException("Can't merge the two blocks!");
|
||||
Append(other);
|
||||
other.DisconnectFromFallThroughAndTargets();
|
||||
other.Parent = null;
|
||||
}
|
||||
|
||||
public bool CanAppend(Block other) {
|
||||
if (other == null || other == this || GetOnlyTarget() != other)
|
||||
return false;
|
||||
// If it's eg. a leave, then don't merge them since it clears the stack.
|
||||
return LastInstr.IsBr() || Instr.IsFallThrough(LastInstr.OpCode);
|
||||
}
|
||||
|
||||
public void Append(Block other) {
|
||||
if (!CanAppend(other))
|
||||
throw new ApplicationException("Can't append the block!");
|
||||
|
||||
RemoveLastBr(); // Get rid of last br/br.s if present
|
||||
|
||||
var newInstructions = new List<Instr>(instructions.Count + other.instructions.Count);
|
||||
AddInstructions(newInstructions, instructions, false);
|
||||
AddInstructions(newInstructions, other.instructions, true);
|
||||
instructions = newInstructions;
|
||||
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
if (other.targets != null)
|
||||
targets = new List<Block>(other.targets);
|
||||
else
|
||||
targets = null;
|
||||
fallThrough = other.fallThrough;
|
||||
UpdateSources();
|
||||
}
|
||||
|
||||
void AddInstructions(IList<Instr> dest, IList<Instr> instrs, bool clone) {
|
||||
for (int i = 0; i < instrs.Count; i++) {
|
||||
var instr = instrs[i];
|
||||
if (instr.OpCode != OpCodes.Nop)
|
||||
dest.Add(clone ? new Instr(instr.Instruction.Clone()) : instr);
|
||||
}
|
||||
}
|
||||
|
||||
// Update each target's Sources property. Must only be called if this isn't in the
|
||||
// Sources list!
|
||||
public void UpdateSources() {
|
||||
if (fallThrough != null)
|
||||
fallThrough.sources.Add(this);
|
||||
if (targets != null) {
|
||||
foreach (var target in targets)
|
||||
target.sources.Add(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if it falls through
|
||||
public bool IsFallThrough() {
|
||||
return targets == null && fallThrough != null;
|
||||
}
|
||||
|
||||
public bool CanFlipConditionalBranch() {
|
||||
return LastInstr.CanFlipConditionalBranch();
|
||||
}
|
||||
|
||||
public void FlipConditionalBranch() {
|
||||
if (fallThrough == null || targets == null || targets.Count != 1)
|
||||
throw new ApplicationException("Invalid bcc block state");
|
||||
LastInstr.FlipConditonalBranch();
|
||||
var oldFallThrough = fallThrough;
|
||||
fallThrough = targets[0];
|
||||
targets[0] = oldFallThrough;
|
||||
}
|
||||
|
||||
// Returns true if it's a conditional branch
|
||||
public bool IsConditionalBranch() {
|
||||
return LastInstr.IsConditionalBranch();
|
||||
}
|
||||
|
||||
public bool IsNopBlock() {
|
||||
if (!IsFallThrough())
|
||||
return false;
|
||||
foreach (var instr in instructions) {
|
||||
if (instr.OpCode.Code != Code.Nop)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Copyright (C) 2011-2015 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 dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public enum BlockType
|
||||
{
|
||||
Normal,
|
||||
Switch,
|
||||
SwitchCase
|
||||
}
|
||||
|
||||
public class SwitchData
|
||||
{
|
||||
public readonly Block Block;
|
||||
public SwitchData(Block switchBlock)
|
||||
{
|
||||
Block = switchBlock;
|
||||
}
|
||||
public int? Key = null;
|
||||
public bool IsKeyHardCoded = false;
|
||||
}
|
||||
|
||||
public class Block : BaseBlock
|
||||
{
|
||||
public Block()
|
||||
{
|
||||
SwitchData = new SwitchData(this);
|
||||
}
|
||||
|
||||
public BlockType BlockType = BlockType.Normal;
|
||||
public SwitchData SwitchData;
|
||||
public bool Processed = false;
|
||||
|
||||
List<Instr> instructions = new List<Instr>();
|
||||
|
||||
// List of all explicit (non-fall-through) targets. It's just one if it's a normal
|
||||
// branch, but if it's a switch, it could be many targets.
|
||||
List<Block> targets;
|
||||
|
||||
// This is the fall through Block (non branch instructions)
|
||||
Block fallThrough;
|
||||
|
||||
// All blocks that fall through or branches to this block
|
||||
List<Block> sources = new List<Block>();
|
||||
|
||||
public Block FallThrough {
|
||||
get { return fallThrough; }
|
||||
set { fallThrough = value; }
|
||||
}
|
||||
|
||||
public List<Block> Targets {
|
||||
get { return targets; }
|
||||
set { targets = value; }
|
||||
}
|
||||
|
||||
public List<Block> Sources {
|
||||
get { return sources; }
|
||||
}
|
||||
|
||||
public Instr FirstInstr {
|
||||
get {
|
||||
if (instructions.Count == 0)
|
||||
Add(new Instr(OpCodes.Nop.ToInstruction()));
|
||||
return instructions[0];
|
||||
}
|
||||
}
|
||||
|
||||
public Instr LastInstr {
|
||||
get {
|
||||
if (instructions.Count == 0)
|
||||
Add(new Instr(OpCodes.Nop.ToInstruction()));
|
||||
return instructions[instructions.Count - 1];
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(Instr instr) {
|
||||
instructions.Add(instr);
|
||||
}
|
||||
|
||||
public void Insert(int index, Instruction instr) {
|
||||
instructions.Insert(index, new Instr(instr));
|
||||
}
|
||||
|
||||
public List<Instr> Instructions {
|
||||
get { return instructions; }
|
||||
}
|
||||
|
||||
// If last instr is a br/br.s, removes it and replaces it with a fall through
|
||||
public void RemoveLastBr() {
|
||||
if (!LastInstr.IsBr())
|
||||
return;
|
||||
|
||||
if (fallThrough != null || (LastInstr.Operand != null && (targets == null || targets.Count != 1)))
|
||||
throw new ApplicationException("Invalid block state when last instr is a br/br.s");
|
||||
fallThrough = LastInstr.Operand != null ? targets[0] : null;
|
||||
targets = null;
|
||||
instructions.RemoveAt(instructions.Count - 1);
|
||||
}
|
||||
|
||||
public void Replace(int index, int num, Instruction instruction) {
|
||||
if (num <= 0)
|
||||
throw new ArgumentOutOfRangeException("num");
|
||||
Remove(index, num);
|
||||
instructions.Insert(index, new Instr(instruction));
|
||||
}
|
||||
|
||||
public void Remove(int index, int num) {
|
||||
if (index + num > instructions.Count)
|
||||
throw new ApplicationException("Overflow");
|
||||
if (num > 0 && index + num == instructions.Count && LastInstr.IsConditionalBranch())
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
instructions.RemoveRange(index, num);
|
||||
}
|
||||
|
||||
public void Remove(IEnumerable<int> indexes) {
|
||||
var instrsToDelete = new List<int>(Utils.Unique(indexes));
|
||||
instrsToDelete.Sort();
|
||||
instrsToDelete.Reverse();
|
||||
foreach (var index in instrsToDelete)
|
||||
Remove(index, 1);
|
||||
}
|
||||
|
||||
// Replace the last instructions with a branch to target
|
||||
public void ReplaceLastInstrsWithBranch(int numInstrs, Block target) {
|
||||
if (numInstrs < 0 || numInstrs > instructions.Count)
|
||||
throw new ApplicationException("Invalid numInstrs to replace with branch");
|
||||
if (target == null)
|
||||
throw new ApplicationException("Invalid new target, it's null");
|
||||
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
if (numInstrs > 0)
|
||||
instructions.RemoveRange(instructions.Count - numInstrs, numInstrs);
|
||||
fallThrough = target;
|
||||
target.sources.Add(this);
|
||||
}
|
||||
|
||||
public void ReplaceLastNonBranchWithBranch(int numInstrs, Block target) {
|
||||
if (LastInstr.IsBr())
|
||||
numInstrs++;
|
||||
ReplaceLastInstrsWithBranch(numInstrs, target);
|
||||
}
|
||||
|
||||
public void ReplaceBccWithBranch(bool isTaken) {
|
||||
Block target = isTaken ? targets[0] : fallThrough;
|
||||
ReplaceLastInstrsWithBranch(1, target);
|
||||
}
|
||||
|
||||
public void ReplaceSwitchWithBranch(Block target) {
|
||||
if (LastInstr.OpCode.Code != Code.Switch)
|
||||
throw new ApplicationException("Last instruction is not a switch");
|
||||
ReplaceLastInstrsWithBranch(1, target);
|
||||
}
|
||||
|
||||
public void SetNewFallThrough(Block newFallThrough) {
|
||||
DisconnectFromFallThrough();
|
||||
fallThrough = newFallThrough;
|
||||
newFallThrough.sources.Add(this);
|
||||
}
|
||||
|
||||
public void SetNewTarget(int index, Block newTarget) {
|
||||
DisconnectFromBlock(targets[index]);
|
||||
targets[index] = newTarget;
|
||||
newTarget.sources.Add(this);
|
||||
}
|
||||
|
||||
public void RemoveDeadBlock() {
|
||||
if (sources.Count != 0)
|
||||
throw new ApplicationException("Trying to remove a non-dead block");
|
||||
RemoveGuaranteedDeadBlock();
|
||||
}
|
||||
|
||||
// Removes a block that has been guaranteed to be dead. This method won't verify
|
||||
// that it really is dead.
|
||||
public void RemoveGuaranteedDeadBlock() {
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
Parent = null;
|
||||
}
|
||||
|
||||
void DisconnectFromFallThroughAndTargets() {
|
||||
DisconnectFromFallThrough();
|
||||
DisconnectFromTargets();
|
||||
}
|
||||
|
||||
void DisconnectFromFallThrough() {
|
||||
if (fallThrough != null) {
|
||||
DisconnectFromBlock(fallThrough);
|
||||
fallThrough = null;
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectFromTargets() {
|
||||
if (targets != null) {
|
||||
foreach (var target in targets)
|
||||
DisconnectFromBlock(target);
|
||||
targets = null;
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectFromBlock(Block target) {
|
||||
if (!target.sources.Remove(this))
|
||||
throw new ApplicationException("Could not remove the block from its target block");
|
||||
}
|
||||
|
||||
public int CountTargets() {
|
||||
int count = fallThrough != null ? 1 : 0;
|
||||
if (targets != null)
|
||||
count += targets.Count;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Returns the target iff it has only ONE target. Else it returns null.
|
||||
public Block GetOnlyTarget() {
|
||||
if (CountTargets() != 1)
|
||||
return null;
|
||||
if (fallThrough != null)
|
||||
return fallThrough;
|
||||
return targets[0];
|
||||
}
|
||||
|
||||
// Returns all targets. FallThrough (if not null) is always returned first!
|
||||
public IEnumerable<Block> GetTargets() {
|
||||
if (fallThrough != null)
|
||||
yield return fallThrough;
|
||||
if (targets != null) {
|
||||
foreach (var block in targets)
|
||||
yield return block;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true iff other is the only block in Sources
|
||||
public bool IsOnlySource(Block other) {
|
||||
return sources.Count == 1 && sources[0] == other;
|
||||
}
|
||||
|
||||
// Returns true if we can merge other with this
|
||||
public bool CanMerge(Block other) {
|
||||
return CanAppend(other) && other.IsOnlySource(this);
|
||||
}
|
||||
|
||||
// Merge two blocks into one
|
||||
public void Merge(Block other) {
|
||||
if (!CanMerge(other))
|
||||
throw new ApplicationException("Can't merge the two blocks!");
|
||||
Append(other);
|
||||
other.DisconnectFromFallThroughAndTargets();
|
||||
other.Parent = null;
|
||||
}
|
||||
|
||||
public bool CanAppend(Block other) {
|
||||
if (other == null || other == this || GetOnlyTarget() != other)
|
||||
return false;
|
||||
// If it's eg. a leave, then don't merge them since it clears the stack.
|
||||
return LastInstr.IsBr() || Instr.IsFallThrough(LastInstr.OpCode);
|
||||
}
|
||||
|
||||
public void Append(Block other) {
|
||||
if (!CanAppend(other))
|
||||
throw new ApplicationException("Can't append the block!");
|
||||
|
||||
RemoveLastBr(); // Get rid of last br/br.s if present
|
||||
|
||||
var newInstructions = new List<Instr>(instructions.Count + other.instructions.Count);
|
||||
AddInstructions(newInstructions, instructions, false);
|
||||
AddInstructions(newInstructions, other.instructions, true);
|
||||
instructions = newInstructions;
|
||||
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
if (other.targets != null)
|
||||
targets = new List<Block>(other.targets);
|
||||
else
|
||||
targets = null;
|
||||
fallThrough = other.fallThrough;
|
||||
UpdateSources();
|
||||
}
|
||||
|
||||
void AddInstructions(IList<Instr> dest, IList<Instr> instrs, bool clone) {
|
||||
for (int i = 0; i < instrs.Count; i++) {
|
||||
var instr = instrs[i];
|
||||
if (instr.OpCode != OpCodes.Nop)
|
||||
dest.Add(clone ? new Instr(instr.Instruction.Clone()) : instr);
|
||||
}
|
||||
}
|
||||
|
||||
// Update each target's Sources property. Must only be called if this isn't in the
|
||||
// Sources list!
|
||||
public void UpdateSources() {
|
||||
if (fallThrough != null)
|
||||
fallThrough.sources.Add(this);
|
||||
if (targets != null) {
|
||||
foreach (var target in targets)
|
||||
target.sources.Add(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if it falls through
|
||||
public bool IsFallThrough() {
|
||||
return targets == null && fallThrough != null;
|
||||
}
|
||||
|
||||
public bool CanFlipConditionalBranch() {
|
||||
return LastInstr.CanFlipConditionalBranch();
|
||||
}
|
||||
|
||||
public void FlipConditionalBranch() {
|
||||
if (fallThrough == null || targets == null || targets.Count != 1)
|
||||
throw new ApplicationException("Invalid bcc block state");
|
||||
LastInstr.FlipConditonalBranch();
|
||||
var oldFallThrough = fallThrough;
|
||||
fallThrough = targets[0];
|
||||
targets[0] = oldFallThrough;
|
||||
}
|
||||
|
||||
// Returns true if it's a conditional branch
|
||||
public bool IsConditionalBranch() {
|
||||
return LastInstr.IsConditionalBranch();
|
||||
}
|
||||
|
||||
public bool IsNopBlock() {
|
||||
if (!IsFallThrough())
|
||||
return false;
|
||||
foreach (var instr in instructions) {
|
||||
if (instr.OpCode.Code != Code.Nop)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -541,8 +541,8 @@ namespace de4dot.blocks.cflow {
|
|||
return CreateUnknown();
|
||||
if (b.Value == 0)
|
||||
return a;
|
||||
if (b.Value < 0 || b.Value >= sizeof(int) * 8)
|
||||
return CreateUnknown();
|
||||
//if (b.Value < 0 || b.Value >= sizeof(int) * 8)
|
||||
// return CreateUnknown();
|
||||
int shift = b.Value;
|
||||
uint validMask = (a.ValidMask << shift) | (uint.MaxValue >> (sizeof(int) * 8 - shift));
|
||||
return new Int32Value(a.Value << shift, validMask);
|
||||
|
@ -553,8 +553,8 @@ namespace de4dot.blocks.cflow {
|
|||
return CreateUnknown();
|
||||
if (b.Value == 0)
|
||||
return a;
|
||||
if (b.Value < 0 || b.Value >= sizeof(int) * 8)
|
||||
return CreateUnknown();
|
||||
//if (b.Value < 0 || b.Value >= sizeof(int) * 8)
|
||||
// return CreateUnknown();
|
||||
int shift = b.Value;
|
||||
uint validMask = a.ValidMask >> shift;
|
||||
if (a.IsBitValid(sizeof(int) * 8 - 1))
|
||||
|
@ -567,8 +567,8 @@ namespace de4dot.blocks.cflow {
|
|||
return CreateUnknown();
|
||||
if (b.Value == 0)
|
||||
return a;
|
||||
if (b.Value < 0 || b.Value >= sizeof(int) * 8)
|
||||
return CreateUnknown();
|
||||
//if (b.Value < 0 || b.Value >= sizeof(int) * 8)
|
||||
// return CreateUnknown();
|
||||
int shift = b.Value;
|
||||
uint validMask = (a.ValidMask >> shift) | (uint.MaxValue << (sizeof(int) * 8 - shift));
|
||||
return new Int32Value((int)((uint)a.Value >> shift), validMask);
|
||||
|
|
|
@ -1,102 +1,102 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{045B96F2-AF80-4C4C-8D27-E38635AC705E}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>de4dot.blocks</RootNamespace>
|
||||
<AssemblyName>de4dot.blocks</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\de4dot.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BaseBlock.cs" />
|
||||
<Compile Include="Block.cs" />
|
||||
<Compile Include="Blocks.cs" />
|
||||
<Compile Include="BlocksSorter.cs" />
|
||||
<Compile Include="cflow\AccessChecker.cs" />
|
||||
<Compile Include="cflow\BlockCflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\BlockDeobfuscator.cs" />
|
||||
<Compile Include="cflow\BlocksCflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\BranchEmulator.cs" />
|
||||
<Compile Include="cflow\CachedCflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\CflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\CflowUtils.cs" />
|
||||
<Compile Include="cflow\ConstantsFolder.cs" />
|
||||
<Compile Include="cflow\DeadCodeRemover.cs" />
|
||||
<Compile Include="cflow\DeadStoreRemover.cs" />
|
||||
<Compile Include="cflow\DupBlockDeobfuscator.cs" />
|
||||
<Compile Include="cflow\IBlocksDeobfuscator.cs" />
|
||||
<Compile Include="cflow\ICflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\InstructionEmulator.cs" />
|
||||
<Compile Include="cflow\Int32Value.cs" />
|
||||
<Compile Include="cflow\Int64Value.cs" />
|
||||
<Compile Include="cflow\MethodCallInliner.cs" />
|
||||
<Compile Include="cflow\MethodCallInlinerBase.cs" />
|
||||
<Compile Include="cflow\Real8Value.cs" />
|
||||
<Compile Include="cflow\StLdlocFixer.cs" />
|
||||
<Compile Include="cflow\SwitchCflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\Value.cs" />
|
||||
<Compile Include="cflow\ValueStack.cs" />
|
||||
<Compile Include="CodeGenerator.cs" />
|
||||
<Compile Include="DeadBlocksRemover.cs" />
|
||||
<Compile Include="DotNetUtils.cs" />
|
||||
<Compile Include="DumpedMethod.cs" />
|
||||
<Compile Include="DumpedMethods.cs" />
|
||||
<Compile Include="FilterHandlerBlock.cs" />
|
||||
<Compile Include="ForwardScanOrder.cs" />
|
||||
<Compile Include="GenericArgsSubstitutor.cs" />
|
||||
<Compile Include="HandlerBlock.cs" />
|
||||
<Compile Include="Instr.cs" />
|
||||
<Compile Include="InstructionListParser.cs" />
|
||||
<Compile Include="MemberDefDict.cs" />
|
||||
<Compile Include="MethodBlocks.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ScopeBlock.cs" />
|
||||
<Compile Include="StackTracePatcher.cs" />
|
||||
<Compile Include="TryBlock.cs" />
|
||||
<Compile Include="TryHandlerBlock.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\dnlib\src\dnlib.csproj">
|
||||
<Project>{FDFC1237-143F-4919-8318-4926901F4639}</Project>
|
||||
<Name>dnlib</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{045B96F2-AF80-4C4C-8D27-E38635AC705E}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>de4dot.blocks</RootNamespace>
|
||||
<AssemblyName>de4dot.blocks</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\de4dot.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BaseBlock.cs" />
|
||||
<Compile Include="Block.cs" />
|
||||
<Compile Include="Blocks.cs" />
|
||||
<Compile Include="BlocksSorter.cs" />
|
||||
<Compile Include="cflow\AccessChecker.cs" />
|
||||
<Compile Include="cflow\BlockCflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\BlockDeobfuscator.cs" />
|
||||
<Compile Include="cflow\BlocksCflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\BranchEmulator.cs" />
|
||||
<Compile Include="cflow\CachedCflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\CflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\CflowUtils.cs" />
|
||||
<Compile Include="cflow\ConstantsFolder.cs" />
|
||||
<Compile Include="cflow\DeadCodeRemover.cs" />
|
||||
<Compile Include="cflow\DeadStoreRemover.cs" />
|
||||
<Compile Include="cflow\DupBlockDeobfuscator.cs" />
|
||||
<Compile Include="cflow\IBlocksDeobfuscator.cs" />
|
||||
<Compile Include="cflow\ICflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\InstructionEmulator.cs" />
|
||||
<Compile Include="cflow\Int32Value.cs" />
|
||||
<Compile Include="cflow\Int64Value.cs" />
|
||||
<Compile Include="cflow\MethodCallInliner.cs" />
|
||||
<Compile Include="cflow\MethodCallInlinerBase.cs" />
|
||||
<Compile Include="cflow\Real8Value.cs" />
|
||||
<Compile Include="cflow\StLdlocFixer.cs" />
|
||||
<Compile Include="cflow\SwitchCflowDeobfuscator.cs" />
|
||||
<Compile Include="cflow\Value.cs" />
|
||||
<Compile Include="cflow\ValueStack.cs" />
|
||||
<Compile Include="CodeGenerator.cs" />
|
||||
<Compile Include="DeadBlocksRemover.cs" />
|
||||
<Compile Include="DotNetUtils.cs" />
|
||||
<Compile Include="DumpedMethod.cs" />
|
||||
<Compile Include="DumpedMethods.cs" />
|
||||
<Compile Include="FilterHandlerBlock.cs" />
|
||||
<Compile Include="ForwardScanOrder.cs" />
|
||||
<Compile Include="GenericArgsSubstitutor.cs" />
|
||||
<Compile Include="HandlerBlock.cs" />
|
||||
<Compile Include="Instr.cs" />
|
||||
<Compile Include="InstructionListParser.cs" />
|
||||
<Compile Include="MemberDefDict.cs" />
|
||||
<Compile Include="MethodBlocks.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ScopeBlock.cs" />
|
||||
<Compile Include="StackTracePatcher.cs" />
|
||||
<Compile Include="TryBlock.cs" />
|
||||
<Compile Include="TryHandlerBlock.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\dnlib\src\dnlib.csproj">
|
||||
<Project>{FDFC1237-143F-4919-8318-4926901F4639}</Project>
|
||||
<Name>dnlib</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
-->
|
||||
</Project>
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
|
@ -14,6 +14,7 @@
|
|||
<FileAlignment>512</FileAlignment>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\de4dot.snk</AssemblyOriginatorKeyFile>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
|
@ -25,6 +26,7 @@
|
|||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
|
@ -36,11 +38,15 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="LinqBridge">
|
||||
<HintPath>..\LinqBridge.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Runtime.Remoting" />
|
||||
|
@ -150,6 +156,29 @@
|
|||
<Compile Include="deobfuscators\CodeWall\randomc\CRandomMersenne.cs" />
|
||||
<Compile Include="deobfuscators\CodeWall\randomc\CRandomMother.cs" />
|
||||
<Compile Include="deobfuscators\CodeWall\StringDecrypter.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\ConstantDecrypter.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\ConstantInliner.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\Deobfuscator.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\ControlFlowSolver.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\LzmaFinder.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\ResourceDecrypter.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\Utils.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Bea\Constants.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Bea\Engine.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Bea\Structs.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Instructions\X86ADD.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Instructions\X86DIV.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Instructions\X86IMUL.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Instructions\X86MOV.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Instructions\X86NEG.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Instructions\X86NOT.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Instructions\X86POP.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Instructions\X86PUSH.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Instructions\X86SUB.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\Instructions\X86XOR.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\UnmanagedBuff.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\X86Instruction.cs" />
|
||||
<Compile Include="deobfuscators\ConfuserEx\x86\X86Method.cs" />
|
||||
<Compile Include="deobfuscators\Confuser\AntiDebugger.cs" />
|
||||
<Compile Include="deobfuscators\Confuser\AntiDumping.cs" />
|
||||
<Compile Include="deobfuscators\Confuser\Arg64ConstantsReader.cs" />
|
||||
|
@ -270,6 +299,7 @@
|
|||
<Compile Include="deobfuscators\InitializedDataCreator.cs" />
|
||||
<Compile Include="deobfuscators\InlinedMethodsFinder.cs" />
|
||||
<Compile Include="deobfuscators\ISimpleDeobfuscator.cs" />
|
||||
<Compile Include="deobfuscators\Lzma.cs" />
|
||||
<Compile Include="deobfuscators\MaxtoCode\CryptDecrypter.cs" />
|
||||
<Compile Include="deobfuscators\MaxtoCode\Decrypter6.cs" />
|
||||
<Compile Include="deobfuscators\MaxtoCode\DecrypterInfo.cs" />
|
||||
|
@ -423,4 +453,4 @@ copy "$(SolutionDir)COPYING" "..\$(OutDir)..\LICENSES"</PostBuildEvent>
|
|||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
|
@ -1,113 +1,113 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.18444
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class CsvmResources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal CsvmResources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("de4dot.code.deobfuscators.Agile_NET.vm.v2.CsvmResources", typeof(CsvmResources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] CSVM1 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CSVM1", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] CSVM2 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CSVM2", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] CSVM3 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CSVM3", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] CSVM4 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CSVM4", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] CSVM5 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CSVM5", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class CsvmResources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal CsvmResources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("de4dot.code.deobfuscators.Agile_NET.vm.v2.CsvmResources", typeof(CsvmResources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] CSVM1 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CSVM1", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] CSVM2 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CSVM2", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] CSVM3 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CSVM3", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] CSVM4 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CSVM4", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] CSVM5 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CSVM5", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,663 @@
|
|||
using de4dot.blocks;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using de4dot.blocks.cflow;
|
||||
using de4dot.code.deobfuscators.ConfuserEx.x86;
|
||||
|
||||
namespace de4dot.code.deobfuscators.ConfuserEx
|
||||
{
|
||||
public class ConstantDecrypterBase
|
||||
{
|
||||
private X86Method _nativeMethod;
|
||||
private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator();
|
||||
|
||||
private int? CalculateKey()
|
||||
{
|
||||
var popValue = _instructionEmulator.Peek();
|
||||
|
||||
if (popValue == null || !popValue.IsInt32() || !(popValue as Int32Value).AllBitsValid())
|
||||
return null;
|
||||
|
||||
_instructionEmulator.Pop();
|
||||
int result = _nativeMethod.Execute(((Int32Value)popValue).Value);
|
||||
return result;
|
||||
}
|
||||
|
||||
public MethodDef Method { get; set; }
|
||||
public MethodDef NativeMethod { get; set; }
|
||||
public byte[] Decrypted { get; set; }
|
||||
public uint Magic1 { get; set; }
|
||||
public uint Magic2 { get; set; }
|
||||
public bool CanRemove { get; set; } = true;
|
||||
|
||||
private uint CalculateMagic(uint index)
|
||||
{
|
||||
_instructionEmulator.Push(new Int32Value((int)index));
|
||||
_nativeMethod = new X86Method(NativeMethod, Method.Module as ModuleDefMD); //TODO: Possible null
|
||||
int? key = CalculateKey();
|
||||
|
||||
uint uint_0 = (uint) key.Value;
|
||||
uint_0 &= 0x3fffffff;
|
||||
uint_0 <<= 2;
|
||||
return uint_0;
|
||||
}
|
||||
public string DecryptString(uint index)
|
||||
{
|
||||
index = CalculateMagic(index);
|
||||
int count = BitConverter.ToInt32(Decrypted, (int)index);
|
||||
return string.Intern(Encoding.UTF8.GetString(Decrypted, (int)index + 4, count));
|
||||
}
|
||||
public T DecryptConstant<T>(uint index)
|
||||
{
|
||||
index = CalculateMagic(index);
|
||||
T[] array = new T[1];
|
||||
Buffer.BlockCopy(Decrypted, (int)index, array, 0, Marshal.SizeOf(typeof(T)));
|
||||
return array[0];
|
||||
}
|
||||
public byte[] DecryptArray(uint index)
|
||||
{
|
||||
index = CalculateMagic(index);
|
||||
int count = BitConverter.ToInt32(Decrypted, (int)index);
|
||||
//int lengt = BitConverter.ToInt32(Decrypted, (int)index+4); we actualy dont need that
|
||||
byte[] buffer = new byte[count - 4];
|
||||
Buffer.BlockCopy(Decrypted, (int)index + 8, buffer, 0, count - 4);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
public class ConstantsDecrypter
|
||||
{
|
||||
TypeDef arrayType;
|
||||
MethodDef constantsDecInitMethod;
|
||||
FieldDef decryptedField, arrayField;
|
||||
List<ConstantDecrypterBase> constantDecrpers = new List<ConstantDecrypterBase>();
|
||||
byte[] decryptedBytes;
|
||||
bool canRemoveLzma = true;
|
||||
|
||||
public bool CanRemoveLzma
|
||||
{
|
||||
get { return canRemoveLzma; }
|
||||
}
|
||||
public TypeDef Type
|
||||
{
|
||||
get { return arrayType; }
|
||||
}
|
||||
public MethodDef Method
|
||||
{
|
||||
get { return constantsDecInitMethod; }
|
||||
}
|
||||
public List<FieldDef> Fields
|
||||
{
|
||||
get { return new List<FieldDef>() { decryptedField, arrayField }; }
|
||||
}
|
||||
public List<ConstantDecrypterBase> Decrypters
|
||||
{
|
||||
get { return constantDecrpers; }
|
||||
}
|
||||
public bool Detected
|
||||
{
|
||||
get { return constantsDecInitMethod != null && decryptedBytes != null && constantDecrpers.Count != 0 && decryptedField != null && arrayField != null; }
|
||||
}
|
||||
|
||||
ModuleDef module;
|
||||
MethodDef lzmaMethod;
|
||||
ISimpleDeobfuscator deobfuscator;
|
||||
public ConstantsDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfuscator deobfsucator)
|
||||
{
|
||||
this.module = module;
|
||||
this.lzmaMethod = lzmaMethod;
|
||||
this.deobfuscator = deobfsucator;
|
||||
}
|
||||
|
||||
public void Find()
|
||||
{
|
||||
var moduleCctor = DotNetUtils.GetModuleTypeCctor(module);
|
||||
if (moduleCctor == null)
|
||||
return;
|
||||
foreach (var inst in moduleCctor.Body.Instructions)
|
||||
{
|
||||
if (inst.OpCode != OpCodes.Call)
|
||||
continue;
|
||||
if (!(inst.Operand is MethodDef))
|
||||
continue;
|
||||
var method = inst.Operand as MethodDef;
|
||||
if (!method.HasBody || !method.IsStatic)
|
||||
continue;
|
||||
if (!DotNetUtils.IsMethod(method, "System.Void", "()"))
|
||||
continue;
|
||||
deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force);
|
||||
if (!isStrDecryptInit(method))
|
||||
continue;
|
||||
constantsDecInitMethod = method;
|
||||
FindStringDecrypters(moduleCctor.DeclaringType);
|
||||
}
|
||||
}
|
||||
|
||||
bool isStrDecryptInit(MethodDef method)
|
||||
{
|
||||
var instructions = method.Body.Instructions;
|
||||
|
||||
if (instructions.Count < 15)
|
||||
return false;
|
||||
|
||||
if (!instructions[0].IsLdcI4())
|
||||
return false;
|
||||
if (!instructions[1].IsStloc()) //uint num = 96u;
|
||||
return false;
|
||||
|
||||
if (!instructions[2].IsLdcI4())
|
||||
return false;
|
||||
if (instructions[0].GetLdcI4Value() != instructions[2].GetLdcI4Value())
|
||||
return false;
|
||||
if (instructions[3].OpCode != OpCodes.Newarr)
|
||||
return false;
|
||||
if (instructions[3].Operand.ToString() != "System.UInt32")
|
||||
return false;
|
||||
if (instructions[4].OpCode != OpCodes.Dup)
|
||||
return false;
|
||||
if (instructions[5].OpCode != OpCodes.Ldtoken)
|
||||
return false;
|
||||
var aField = instructions[5].Operand as FieldDef;
|
||||
if (aField == null)
|
||||
return false;
|
||||
if (aField.InitialValue == null)
|
||||
return false;
|
||||
if (aField.Attributes != (FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.HasFieldRVA))
|
||||
return false;
|
||||
if (instructions[6].OpCode != OpCodes.Call)
|
||||
return false;
|
||||
if (instructions[6].Operand.ToString() != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)")
|
||||
return false;
|
||||
if (!instructions[7].IsStloc()) // uint[] array = new uint[] {.....};
|
||||
return false;
|
||||
|
||||
var l = instructions.Count;
|
||||
if (!instructions[l - 4].IsLdloc())
|
||||
return false;
|
||||
if (instructions[l - 3].OpCode != OpCodes.Call)
|
||||
return false;
|
||||
if (instructions[l - 3].Operand != lzmaMethod)
|
||||
return false;
|
||||
if (instructions[l - 2].OpCode != OpCodes.Stsfld) //<Module>.byte_0 = <Module>.smethod_0(array4);
|
||||
return false;
|
||||
var dField = instructions[l - 2].Operand as FieldDef;
|
||||
if (dField == null)
|
||||
return false;
|
||||
try
|
||||
{
|
||||
DecryptArray(ConvertArray<uint, byte>(aField.InitialValue));
|
||||
}
|
||||
catch (Exception e) { Console.WriteLine(e.Message); canRemoveLzma = false; return false; }
|
||||
arrayField = aField;
|
||||
arrayType = DotNetUtils.GetType(module, aField.FieldSig.Type);
|
||||
decryptedField = dField;
|
||||
return true;
|
||||
}
|
||||
|
||||
private T[] ConvertArray<T, T1>(T1[] array)
|
||||
{
|
||||
int l = Marshal.SizeOf(typeof(T));
|
||||
int l1 = Marshal.SizeOf(typeof(T1));
|
||||
var buffer = new T[(array.Length * l1) / l];
|
||||
Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private void DecryptArray(uint[] array)
|
||||
{
|
||||
uint num = 1680u; // array size?
|
||||
uint[] array2 = new uint[16];
|
||||
uint num2 = 3186233426u;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
num2 ^= num2 >> 12;
|
||||
num2 ^= num2 << 25;
|
||||
num2 ^= num2 >> 27;
|
||||
array2[i] = num2;
|
||||
}
|
||||
int num3 = 0;
|
||||
int num4 = 0;
|
||||
uint[] array3 = new uint[16];
|
||||
byte[] array4 = new byte[num * 4u];
|
||||
while ((long)num3 < (long)((ulong)num))
|
||||
{
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
array3[j] = array[num3 + j];
|
||||
}
|
||||
uint num5 = array3[9] >> 23;
|
||||
array3[0] = array3[0] * 865957733u;
|
||||
array3[9] = array3[9] << 9;
|
||||
array3[9] = (array3[9] | num5);
|
||||
array3[3] = (array3[3] ^ 4272581837u);
|
||||
array3[8] = (array3[8] ^ array2[8]);
|
||||
num5 = (array3[14] & 3955221806u);
|
||||
num5 *= 1105615415u;
|
||||
array3[14] = (array3[14] & 339745489u);
|
||||
array3[14] = (array3[14] | (array3[0] & 3955221806u));
|
||||
array3[15] = (array3[15] ^ array3[8]);
|
||||
array3[0] = (array3[0] & 339745489u);
|
||||
array3[0] = (array3[0] | num5 * 809663367u);
|
||||
array3[14] = (array3[14] ^ 1753824488u);
|
||||
num5 = array3[11] << 18;
|
||||
array3[11] = array3[11] >> 14;
|
||||
array3[1] = array3[1] - 301755752u;
|
||||
array3[11] = (array3[11] | num5);
|
||||
uint num6 = array3[14] << 3;
|
||||
num5 = (array3[9] & 2370496838u);
|
||||
array3[10] = array3[10] - 4147007853u;
|
||||
uint num7 = array3[14] << 4;
|
||||
num5 *= 2575732745u;
|
||||
num6 += array3[14] << 4;
|
||||
array3[9] = (array3[9] & 1924470457u);
|
||||
array3[9] = (array3[9] | (array3[2] & 2370496838u));
|
||||
array3[12] = array3[12] - array3[6];
|
||||
array3[2] = (array3[2] & 1924470457u);
|
||||
array3[0] = array3[0] - array3[4];
|
||||
array3[2] = (array3[2] | num5 * 593100345u);
|
||||
num5 = array3[6] * 993944645u;
|
||||
array3[6] = array3[5];
|
||||
uint num8 = array3[14] * 19u;
|
||||
array3[5] = num5 * 679153293u;
|
||||
num5 = (array3[1] & 4141471236u);
|
||||
num8 += array3[5] * 67u;
|
||||
num8 += array3[10] * 43u;
|
||||
array3[13] = (array3[13] ^ array2[13]);
|
||||
array3[1] = (array3[1] & 153496059u);
|
||||
num6 += array3[5] * 92u;
|
||||
num7 += array3[5] * 57u;
|
||||
num7 += array3[10] * 37u;
|
||||
num5 *= 831032307u;
|
||||
array3[1] = (array3[1] | (array3[12] & 4141471236u));
|
||||
array3[0] = (array3[0] ^ array2[0]);
|
||||
array3[12] = (array3[12] & 153496059u);
|
||||
array3[11] = (array3[11] ^ array2[11]);
|
||||
num6 += array3[10] << 6;
|
||||
array3[12] = (array3[12] | num5 * 419693883u);
|
||||
num7 += array3[15] * 107u;
|
||||
array3[3] = (array3[3] ^ array2[3]);
|
||||
num5 = (array3[13] & 2032982899u);
|
||||
array3[13] = (array3[13] & 2261984396u);
|
||||
num5 *= 3754449215u;
|
||||
num8 += array3[15] * 125u;
|
||||
num6 += array3[15] * 179u;
|
||||
array3[13] = (array3[13] | (array3[7] & 2032982899u));
|
||||
array3[7] = (array3[7] & 2261984396u);
|
||||
array3[7] = (array3[7] | num5 * 1302730431u);
|
||||
num5 = array3[14] * 7u;
|
||||
num5 += array3[5] * 25u;
|
||||
array3[12] = (array3[12] ^ ~array3[4]);
|
||||
num5 += array3[10] << 4;
|
||||
array3[5] = num6;
|
||||
array3[10] = num7;
|
||||
num6 = array3[2] >> 19;
|
||||
num7 = array3[11] >> 19;
|
||||
num5 += array3[15] * 46u;
|
||||
array3[15] = num8;
|
||||
num8 = array3[0] << 2;
|
||||
array3[2] = array3[2] << 13;
|
||||
array3[11] = array3[11] << 13;
|
||||
array3[14] = num5;
|
||||
array3[0] = array3[0] >> 30;
|
||||
array3[2] = (array3[2] | num6);
|
||||
array3[6] = (array3[6] ^ 825592879u);
|
||||
array3[2] = (array3[2] ^ array2[2]);
|
||||
array3[11] = (array3[11] | num7);
|
||||
num5 = array3[15] << 1;
|
||||
array3[0] = (array3[0] | num8);
|
||||
num8 = array3[15] * 23u;
|
||||
num7 = array3[15] * 7u;
|
||||
num8 += array3[7] * 45u;
|
||||
num7 += array3[7] * 11u;
|
||||
num5 += array3[15];
|
||||
array3[9] = (array3[9] ^ array2[9]);
|
||||
num5 += array3[7] * 7u;
|
||||
num8 += array3[13] << 7;
|
||||
array3[3] = (array3[3] ^ ~array3[8]);
|
||||
array3[10] = array3[10] * 2256145475u;
|
||||
num6 = array3[15] << 2;
|
||||
num6 += array3[15];
|
||||
num7 += array3[13] << 5;
|
||||
num7 += array3[1] << 1;
|
||||
num6 += array3[7] << 2;
|
||||
num6 += array3[7] << 3;
|
||||
num8 += array3[13];
|
||||
num8 += array3[1] * 143u;
|
||||
num5 += array3[13] << 2;
|
||||
num6 += array3[13] << 1;
|
||||
num7 += array3[1] << 5;
|
||||
num5 += array3[13] << 4;
|
||||
array3[15] = num7;
|
||||
num5 += array3[1] * 23u;
|
||||
num6 += array3[13] << 5;
|
||||
array3[7] = num5;
|
||||
num6 += array3[1] * 39u;
|
||||
array3[1] = num8;
|
||||
num8 = array3[1] * 26u;
|
||||
num7 = array3[1] << 6;
|
||||
array3[7] = (array3[7] ^ array2[7]);
|
||||
array3[13] = num6;
|
||||
num5 = array3[1] << 1;
|
||||
num5 += array3[1] << 3;
|
||||
num6 = array3[1] * 13u;
|
||||
num5 += array3[13] << 1;
|
||||
num7 += array3[1];
|
||||
num6 += array3[13] * 45u;
|
||||
array3[9] = (array3[9] ^ 786150263u);
|
||||
num8 += array3[13] * 88u;
|
||||
num6 += array3[2] * 67u;
|
||||
array3[8] = (array3[8] ^ 110539985u);
|
||||
num5 += array3[13] << 5;
|
||||
num6 += array3[11] << 1;
|
||||
num8 += array3[2] * 133u;
|
||||
array3[10] = (array3[10] ^ array2[10]);
|
||||
num8 += array3[11] << 6;
|
||||
array3[15] = array3[15] - 2992485470u;
|
||||
array3[0] = array3[0] - array3[6];
|
||||
num6 += array3[11] << 5;
|
||||
num5 += array3[2] * 51u;
|
||||
num5 += array3[11] * 25u;
|
||||
array3[12] = (array3[12] ^ ~array3[3]);
|
||||
num7 += array3[13] * 222u;
|
||||
array3[13] = num5;
|
||||
array3[1] = num6;
|
||||
array3[13] = array3[13] * 3578289835u;
|
||||
num6 = (array3[10] & 381620437u);
|
||||
array3[10] = (array3[10] & 3913346858u);
|
||||
num5 = array3[0] * 14u;
|
||||
num7 += array3[2] * 333u;
|
||||
array3[2] = num8;
|
||||
num5 += array3[3] * 11u;
|
||||
array3[10] = (array3[10] | (array3[14] & 381620437u));
|
||||
array3[7] = (array3[7] ^ ~array3[4]);
|
||||
num6 *= 3323466531u;
|
||||
array3[14] = (array3[14] & 3913346858u);
|
||||
num8 = array3[0] << 2;
|
||||
num5 += array3[5] * 54u;
|
||||
array3[14] = (array3[14] | num6 * 1991488651u);
|
||||
num7 += array3[11] * 164u;
|
||||
num6 = (array3[2] & 2341248020u);
|
||||
array3[11] = num7;
|
||||
num7 = array3[0] * 11u;
|
||||
num8 += array3[0] << 4;
|
||||
array3[2] = (array3[2] & 1953719275u);
|
||||
num8 += array3[3] << 2;
|
||||
array3[2] = (array3[2] | (array3[11] & 2341248020u));
|
||||
num8 += array3[3] << 4;
|
||||
num6 *= 4030567715u;
|
||||
array3[14] = (array3[14] ^ array2[14]);
|
||||
array3[11] = (array3[11] & 1953719275u);
|
||||
array3[11] = (array3[11] | num6 * 62866059u);
|
||||
num6 = array3[0] << 2;
|
||||
num8 += array3[5] * 90u;
|
||||
num7 += array3[3] << 4;
|
||||
num7 += array3[5] << 2;
|
||||
num6 += array3[0];
|
||||
array3[12] = (array3[12] ^ array2[12]);
|
||||
num7 += array3[5] << 6;
|
||||
num8 += array3[13] * 117u;
|
||||
array3[9] = (array3[9] ^ array3[5]);
|
||||
num5 += array3[13] * 52u;
|
||||
num6 += array3[3] << 1;
|
||||
num6 += array3[3];
|
||||
num7 += array3[13] * 126u;
|
||||
num6 += array3[5] << 4;
|
||||
num6 += array3[5];
|
||||
array3[5] = num8;
|
||||
array3[3] = num5;
|
||||
num6 += array3[13] * 11u;
|
||||
array3[0] = num6;
|
||||
array3[13] = num7;
|
||||
num6 = array3[6] << 1;
|
||||
num6 += array3[15] << 1;
|
||||
num5 = array3[12] << 29;
|
||||
num6 += array3[15] << 2;
|
||||
num7 = array3[7] << 12;
|
||||
array3[11] = array3[11] - array3[10];
|
||||
array3[7] = array3[7] >> 20;
|
||||
array3[12] = array3[12] >> 3;
|
||||
array3[12] = (array3[12] | num5);
|
||||
array3[14] = (array3[14] ^ ~array3[8]);
|
||||
array3[1] = (array3[1] ^ array2[1]);
|
||||
array3[1] = (array3[1] ^ 3215842197u);
|
||||
num8 = array3[6] * 7u;
|
||||
array3[7] = (array3[7] | num7);
|
||||
num8 += array3[15] * 26u;
|
||||
num5 = array3[6] << 2;
|
||||
num5 += array3[15] << 2;
|
||||
array3[9] = array3[9] - array3[2];
|
||||
num7 = array3[6] << 2;
|
||||
array3[4] = (array3[4] ^ array2[4]);
|
||||
num6 += array3[4] << 4;
|
||||
array3[3] = (array3[3] ^ 1425746098u);
|
||||
num5 += array3[15];
|
||||
num8 += array3[4] * 69u;
|
||||
num5 += array3[4] * 15u;
|
||||
num7 += array3[6];
|
||||
num6 += array3[1] * 15u;
|
||||
num8 += array3[1] * 63u;
|
||||
array3[6] = num6;
|
||||
num7 += array3[15] * 11u;
|
||||
num7 += array3[4] * 31u;
|
||||
num7 += array3[1] * 30u;
|
||||
num5 += array3[1] << 4;
|
||||
array3[5] = (array3[5] ^ array2[5]);
|
||||
array3[4] = num7;
|
||||
num7 = (array3[5] & 2375297997u);
|
||||
array3[6] = (array3[6] ^ array2[6]);
|
||||
num7 *= 3574473459u;
|
||||
array3[15] = num5;
|
||||
array3[5] = (array3[5] & 1919669298u);
|
||||
array3[5] = (array3[5] | (array3[13] & 2375297997u));
|
||||
array3[1] = num8;
|
||||
array3[15] = (array3[15] ^ array2[15]);
|
||||
num8 = array3[0] << 5;
|
||||
array3[13] = (array3[13] & 1919669298u);
|
||||
array3[13] = (array3[13] | num7 * 2683487803u);
|
||||
array3[0] = array3[0] >> 27;
|
||||
array3[0] = (array3[0] | num8);
|
||||
for (int k = 0; k < 16; k++)
|
||||
{
|
||||
uint num9 = array3[k];
|
||||
array4[num4++] = (byte)num9;
|
||||
array4[num4++] = (byte)(num9 >> 8);
|
||||
array4[num4++] = (byte)(num9 >> 16);
|
||||
array4[num4++] = (byte)(num9 >> 24);
|
||||
array2[k] ^= num9;
|
||||
}
|
||||
num3 += 16;
|
||||
}
|
||||
decryptedBytes = Lzma.Decompress(array4);
|
||||
}
|
||||
|
||||
private void FindStringDecrypters(TypeDef type)
|
||||
{
|
||||
foreach (var method in type.Methods)
|
||||
{
|
||||
if (!method.HasBody)
|
||||
continue;
|
||||
if (!(method.Signature.ContainsGenericParameter))
|
||||
continue;
|
||||
var sig = method.MethodSig;
|
||||
if (sig == null)
|
||||
continue;
|
||||
if (sig.Params.Count != 1)
|
||||
continue;
|
||||
if (sig.Params[0].GetElementType() != ElementType.U4)
|
||||
continue;
|
||||
if (!(sig.RetType.RemovePinnedAndModifiers() is GenericMVar))
|
||||
continue;
|
||||
if (sig.GenParamCount != 1)
|
||||
continue;
|
||||
deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force);
|
||||
IsStringDecrypter(method);
|
||||
}
|
||||
}
|
||||
|
||||
string[] strDecryptCalledMethods = {
|
||||
"System.Text.Encoding System.Text.Encoding::get_UTF8()",
|
||||
"System.String System.Text.Encoding::GetString(System.Byte[],System.Int32,System.Int32)",
|
||||
"System.Array System.Array::CreateInstance(System.Type,System.Int32)",
|
||||
"System.String System.String::Intern(System.String)",
|
||||
"System.Void System.Buffer::BlockCopy(System.Array,System.Int32,System.Array,System.Int32,System.Int32)",
|
||||
"System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)",
|
||||
"System.Type System.Type::GetElementType()"
|
||||
};
|
||||
|
||||
private void IsStringDecrypter(MethodDef method)
|
||||
{
|
||||
var instr = method.Body.Instructions;
|
||||
if (instr.Count < 25)
|
||||
return;
|
||||
|
||||
int i = 0;
|
||||
|
||||
if (!instr[i++].IsLdarg())
|
||||
return;
|
||||
|
||||
if (instr[i].OpCode != OpCodes.Call)
|
||||
return;
|
||||
|
||||
var nativeMethod = instr[i++].Operand as MethodDef;
|
||||
|
||||
if (nativeMethod == null || !nativeMethod.IsStatic || !nativeMethod.IsNative)
|
||||
return;
|
||||
if (!DotNetUtils.IsMethod(nativeMethod, "System.Int32", "(System.Int32)"))
|
||||
return;
|
||||
|
||||
if (!instr[i++].IsStarg()) //uint_0 = (uint_0 * 2857448701u ^ 1196001109u);
|
||||
return;
|
||||
|
||||
if (!instr[i++].IsLdarg())
|
||||
return;
|
||||
if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 0x1E)
|
||||
return;
|
||||
if (instr[i++].OpCode != OpCodes.Shr_Un)
|
||||
return;
|
||||
if (!instr[i++].IsStloc()) //uint num = uint_0 >> 30;
|
||||
return;
|
||||
i++;
|
||||
//TODO: Implement
|
||||
//if (!instr[10].IsLdloca())
|
||||
// return;
|
||||
if (instr[i++].OpCode != OpCodes.Initobj)
|
||||
return;
|
||||
if (!instr[i++].IsLdarg())
|
||||
return;
|
||||
if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 0x3FFFFFFF)
|
||||
return;
|
||||
if (instr[i++].OpCode != OpCodes.And)
|
||||
return;
|
||||
if (!instr[i++].IsStarg()) //uint_0 &= 1073741823u;
|
||||
return;
|
||||
|
||||
if (!instr[i++].IsLdarg())
|
||||
return;
|
||||
if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 2)
|
||||
return;
|
||||
if (instr[i++].OpCode != OpCodes.Shl)
|
||||
return;
|
||||
if (!instr[i++].IsStarg()) //uint_0 <<= 2;
|
||||
return;
|
||||
|
||||
foreach (var mtd in strDecryptCalledMethods)
|
||||
if (!DotNetUtils.CallsMethod(method, mtd))
|
||||
return;
|
||||
//TODO: Implement
|
||||
//if (!DotNetUtils.LoadsField(method, decryptedField))
|
||||
// return;
|
||||
constantDecrpers.Add(new ConstantDecrypterBase()
|
||||
{
|
||||
Decrypted = decryptedBytes,
|
||||
Method = method,
|
||||
NativeMethod = nativeMethod,
|
||||
});
|
||||
}
|
||||
|
||||
static bool VerifyGenericArg(MethodSpec gim, ElementType etype)
|
||||
{
|
||||
if (gim == null)
|
||||
return false;
|
||||
var gims = gim.GenericInstMethodSig;
|
||||
if (gims == null || gims.GenericArguments.Count != 1)
|
||||
return false;
|
||||
return gims.GenericArguments[0].GetElementType() == etype;
|
||||
}
|
||||
public string DecryptString(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.String))
|
||||
return null;
|
||||
return info.DecryptString(magic1);
|
||||
}
|
||||
public object DecryptSByte(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.I1))
|
||||
return null;
|
||||
return info.DecryptConstant<sbyte>(magic1);
|
||||
}
|
||||
public object DecryptByte(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.U1))
|
||||
return null;
|
||||
return info.DecryptConstant<byte>(magic1);
|
||||
}
|
||||
public object DecryptInt16(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.I2))
|
||||
return null;
|
||||
return info.DecryptConstant<short>(magic1);
|
||||
}
|
||||
public object DecryptUInt16(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.U2))
|
||||
return null;
|
||||
return info.DecryptConstant<ushort>(magic1);
|
||||
}
|
||||
public object DecryptInt32(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.I4))
|
||||
return null;
|
||||
return info.DecryptConstant<int>(magic1);
|
||||
}
|
||||
public object DecryptUInt32(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.U4))
|
||||
return null;
|
||||
return info.DecryptConstant<uint>(magic1);
|
||||
}
|
||||
public object DecryptInt64(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.I8))
|
||||
return null;
|
||||
return info.DecryptConstant<long>(magic1);
|
||||
}
|
||||
public object DecryptUInt64(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.U8))
|
||||
return null;
|
||||
return info.DecryptConstant<ulong>(magic1);
|
||||
}
|
||||
public object DecryptSingle(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.R4))
|
||||
return null;
|
||||
return info.DecryptConstant<float>(magic1);
|
||||
}
|
||||
public object DecryptDouble(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.R8))
|
||||
return null;
|
||||
return info.DecryptConstant<double>(magic1);
|
||||
}
|
||||
public object DecryptArray(ConstantDecrypterBase info, MethodSpec gim, uint magic1)
|
||||
{
|
||||
if (!VerifyGenericArg(gim, ElementType.SZArray))
|
||||
return null;
|
||||
return info.DecryptArray(magic1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.blocks;
|
||||
using de4dot.blocks.cflow;
|
||||
using System;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.code.deobfuscators.ConfuserEx
|
||||
{
|
||||
class ConstantsInliner : IBlocksDeobfuscator
|
||||
{
|
||||
Blocks blocks;
|
||||
SByteValueInliner sbyteValueInliner;
|
||||
ByteValueInliner byteValueInliner;
|
||||
Int16ValueInliner int16ValueInliner;
|
||||
UInt16ValueInliner uint16ValueInliner;
|
||||
Int32ValueInliner int32ValueInliner;
|
||||
UInt32ValueInliner uint32ValueInliner;
|
||||
Int64ValueInliner int64ValueInliner;
|
||||
UInt64ValueInliner uint64ValueInliner;
|
||||
SingleValueInliner singleValueInliner;
|
||||
DoubleValueInliner doubleValueInliner;
|
||||
ArrayValueInliner arrayValueInliner;
|
||||
|
||||
public bool ExecuteIfNotModified { get; set; }
|
||||
|
||||
public ConstantsInliner(SByteValueInliner sbyteValueInliner, ByteValueInliner byteValueInliner,
|
||||
Int16ValueInliner int16ValueInliner, UInt16ValueInliner uint16ValueInliner, Int32ValueInliner int32ValueInliner,
|
||||
UInt32ValueInliner uint32ValueInliner, Int64ValueInliner int64ValueInliner, UInt64ValueInliner uint64ValueInliner,
|
||||
SingleValueInliner singleValueInliner, DoubleValueInliner doubleValueInliner, ArrayValueInliner arrayValueInliner)
|
||||
{
|
||||
this.sbyteValueInliner = sbyteValueInliner;
|
||||
this.byteValueInliner = byteValueInliner;
|
||||
this.int16ValueInliner = int16ValueInliner;
|
||||
this.uint16ValueInliner = uint16ValueInliner;
|
||||
this.int32ValueInliner = int32ValueInliner;
|
||||
this.uint32ValueInliner = uint32ValueInliner;
|
||||
this.int64ValueInliner = int64ValueInliner;
|
||||
this.uint64ValueInliner = uint64ValueInliner;
|
||||
this.singleValueInliner = singleValueInliner;
|
||||
this.doubleValueInliner = doubleValueInliner;
|
||||
this.arrayValueInliner = arrayValueInliner;
|
||||
}
|
||||
|
||||
public void DeobfuscateBegin(Blocks blocks)
|
||||
{
|
||||
this.blocks = blocks;
|
||||
}
|
||||
|
||||
public bool Deobfuscate(List<Block> allBlocks)
|
||||
{
|
||||
bool modified = false;
|
||||
foreach (var block in allBlocks)
|
||||
{
|
||||
modified |= sbyteValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
modified |= byteValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
modified |= int16ValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
modified |= uint16ValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
modified |= int32ValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
modified |= uint32ValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
modified |= int64ValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
modified |= uint64ValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
modified |= singleValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
modified |= doubleValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
modified |= arrayValueInliner.Decrypt(blocks.Method, allBlocks) != 0;
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
public class SByteValueInliner : ValueInlinerBase<sbyte>
|
||||
{
|
||||
protected override void InlineReturnValues(IList<CallResult> callResults)
|
||||
{
|
||||
foreach (var callResult in callResults)
|
||||
{
|
||||
var block = callResult.block;
|
||||
int num = callResult.callEndIndex - callResult.callStartIndex + 1;
|
||||
|
||||
block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue));
|
||||
RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.SByte");
|
||||
Logger.v("Decrypted sbyte: {0}", callResult.returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class ByteValueInliner : ValueInlinerBase<byte>
|
||||
{
|
||||
protected override void InlineReturnValues(IList<CallResult> callResults)
|
||||
{
|
||||
foreach (var callResult in callResults)
|
||||
{
|
||||
var block = callResult.block;
|
||||
int num = callResult.callEndIndex - callResult.callStartIndex + 1;
|
||||
|
||||
block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue));
|
||||
RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.Byte");
|
||||
Logger.v("Decrypted byte: {0}", callResult.returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class Int16ValueInliner : ValueInlinerBase<short>
|
||||
{
|
||||
protected override void InlineReturnValues(IList<CallResult> callResults)
|
||||
{
|
||||
foreach (var callResult in callResults)
|
||||
{
|
||||
var block = callResult.block;
|
||||
int num = callResult.callEndIndex - callResult.callStartIndex + 1;
|
||||
|
||||
block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue));
|
||||
RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.Int16");
|
||||
Logger.v("Decrypted int16: {0}", callResult.returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class UInt16ValueInliner : ValueInlinerBase<ushort>
|
||||
{
|
||||
protected override void InlineReturnValues(IList<CallResult> callResults)
|
||||
{
|
||||
foreach (var callResult in callResults)
|
||||
{
|
||||
var block = callResult.block;
|
||||
int num = callResult.callEndIndex - callResult.callStartIndex + 1;
|
||||
|
||||
block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue));
|
||||
RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.UInt16");
|
||||
Logger.v("Decrypted uint16: {0}", callResult.returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class UInt32ValueInliner : ValueInlinerBase<uint>
|
||||
{
|
||||
protected override void InlineReturnValues(IList<CallResult> callResults)
|
||||
{
|
||||
foreach (var callResult in callResults)
|
||||
{
|
||||
var block = callResult.block;
|
||||
int num = callResult.callEndIndex - callResult.callStartIndex + 1;
|
||||
|
||||
block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue));
|
||||
RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.UInt32");
|
||||
Logger.v("Decrypted uint32: {0}", callResult.returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class UInt64ValueInliner : ValueInlinerBase<ulong>
|
||||
{
|
||||
protected override void InlineReturnValues(IList<CallResult> callResults)
|
||||
{
|
||||
foreach (var callResult in callResults)
|
||||
{
|
||||
var block = callResult.block;
|
||||
int num = callResult.callEndIndex - callResult.callStartIndex + 1;
|
||||
|
||||
block.Replace(callResult.callStartIndex, num, OpCodes.Ldc_I8.ToInstruction((long)callResult.returnValue));
|
||||
RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.UInt64");
|
||||
Logger.v("Decrypted uint64: {0}", callResult.returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class ArrayValueInliner : ValueInlinerBase<Array>
|
||||
{
|
||||
InitializedDataCreator initializedDataCreator;
|
||||
|
||||
public ArrayValueInliner(InitializedDataCreator initializedDataCreator) { this.initializedDataCreator = initializedDataCreator; }
|
||||
protected override void InlineReturnValues(IList<CallResult> callResults)
|
||||
{
|
||||
foreach (var callResult in callResults)
|
||||
{
|
||||
var block = callResult.block;
|
||||
int num = callResult.callEndIndex - callResult.callStartIndex + 1;
|
||||
|
||||
var generic = (callResult.GetMethodRef() as MethodSpec).GenericInstMethodSig.GenericArguments;
|
||||
ITypeDefOrRef sig = generic[0].Next.ToTypeDefOrRef();
|
||||
|
||||
initializedDataCreator.AddInitializeArrayCode(block, callResult.callStartIndex, num, sig, callResult.returnValue as byte[]);
|
||||
RemoveUnboxInstruction(block, callResult.callStartIndex + 1, sig.ToString()); //TODO: sig.ToString() ??
|
||||
Logger.v("Decrypted array <{1}>: {0}", callResult.returnValue, sig.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,322 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using de4dot.blocks;
|
||||
using de4dot.blocks.cflow;
|
||||
using de4dot.code.deobfuscators.ConfuserEx.x86;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.code.deobfuscators.ConfuserEx
|
||||
{
|
||||
class ControlFlowSolver : IBlocksDeobfuscator
|
||||
{
|
||||
public bool ExecuteIfNotModified { get; }
|
||||
private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator();
|
||||
|
||||
private Blocks _blocks;
|
||||
private X86Method _nativeMethod;
|
||||
private Local _switchKey;
|
||||
|
||||
private int? CalculateKey()
|
||||
{
|
||||
var popValue = _instructionEmulator.Peek();
|
||||
|
||||
if (popValue == null || !popValue.IsInt32() || !(popValue as Int32Value).AllBitsValid())
|
||||
return null;
|
||||
|
||||
_instructionEmulator.Pop();
|
||||
int result = _nativeMethod.Execute(((Int32Value)popValue).Value);
|
||||
return result;
|
||||
}
|
||||
|
||||
private int CalculateSwitchCaseIndex(Block block, int nativeKey)
|
||||
{
|
||||
_instructionEmulator.Push(new Int32Value(nativeKey));
|
||||
_instructionEmulator.Emulate(block.Instructions, block.SwitchData.IsKeyHardCoded ? 2 : 1, block.Instructions.Count - 1);
|
||||
|
||||
var popValue = _instructionEmulator.Peek();
|
||||
_instructionEmulator.Pop();
|
||||
return ((Int32Value)popValue).Value;
|
||||
}
|
||||
|
||||
private void ProcessHardcodedSwitch(Block switchBlock) // a single-case switch
|
||||
{
|
||||
var targets = switchBlock.Targets;
|
||||
|
||||
_instructionEmulator.Push(new Int32Value(switchBlock.SwitchData.Key.Value));
|
||||
int? key = CalculateKey();
|
||||
|
||||
if (!key.HasValue)
|
||||
throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
|
||||
|
||||
int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
|
||||
|
||||
if (targets.Count < switchCaseIndex)
|
||||
throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
|
||||
|
||||
var targetBlock = targets[switchCaseIndex];
|
||||
targetBlock.SwitchData.Key = key;
|
||||
|
||||
switchBlock.Instructions.Clear();
|
||||
switchBlock.ReplaceLastNonBranchWithBranch(0, targetBlock);
|
||||
}
|
||||
|
||||
private void ProcessBlock(List<Block> switchCaseBlocks, Block block, Block switchBlock)
|
||||
{
|
||||
var targets = switchBlock.Targets;
|
||||
|
||||
_instructionEmulator.Emulate(block.Instructions, 0, block.Instructions.Count);
|
||||
|
||||
if (_instructionEmulator.Peek().IsUnknown())
|
||||
throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN");
|
||||
|
||||
int? key = CalculateKey();
|
||||
|
||||
if (!key.HasValue)
|
||||
throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
|
||||
|
||||
int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
|
||||
|
||||
if (targets.Count < switchCaseIndex)
|
||||
throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
|
||||
|
||||
var targetBlock = targets[switchCaseIndex];
|
||||
targetBlock.SwitchData.Key = key;
|
||||
|
||||
block.Add(new Instr(OpCodes.Pop.ToInstruction())); // neutralize the arithmetics and leave de4dot to remove them
|
||||
block.ReplaceLastNonBranchWithBranch(0, targetBlock);
|
||||
|
||||
ProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, key.Value);
|
||||
|
||||
block.Processed = true;
|
||||
}
|
||||
|
||||
private void ProcessTernaryBlock(List<Block> switchCaseBlocks, Block ternaryBlock, Block switchBlock)
|
||||
{
|
||||
var targets = switchBlock.Targets;
|
||||
|
||||
for (int i = 0; i < 2; i++) // loop both source blocks
|
||||
{
|
||||
var sourceBlock = ternaryBlock.Sources[0];
|
||||
|
||||
if(ternaryBlock.SwitchData.Key.HasValue) // single instruction: pop -- no key!
|
||||
SetLocalSwitchKey(ternaryBlock.SwitchData.Key.Value); // set old key for both iterations!
|
||||
|
||||
_instructionEmulator.Emulate(sourceBlock.Instructions, 0, sourceBlock.Instructions.Count);
|
||||
_instructionEmulator.Emulate(ternaryBlock.Instructions, 0, ternaryBlock.Instructions.Count);
|
||||
|
||||
if (_instructionEmulator.Peek().IsUnknown())
|
||||
throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN");
|
||||
|
||||
int? key = CalculateKey();
|
||||
|
||||
if (!key.HasValue)
|
||||
throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
|
||||
|
||||
int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
|
||||
|
||||
if (targets.Count < switchCaseIndex)
|
||||
throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
|
||||
|
||||
var targetBlock = targets[switchCaseIndex];
|
||||
targetBlock.SwitchData.Key = key;
|
||||
|
||||
sourceBlock.Instructions[sourceBlock.Instructions.Count - 1] = new Instr(OpCodes.Pop.ToInstruction());
|
||||
sourceBlock.ReplaceLastNonBranchWithBranch(0, targets[switchCaseIndex]);
|
||||
|
||||
ProcessFallThroughs(switchCaseBlocks, switchBlock, targets[switchCaseIndex], key.Value);
|
||||
// the second source block now becomes the first one
|
||||
}
|
||||
|
||||
//switchCaseBlock.Instructions.Clear();
|
||||
ternaryBlock.Add(new Instr(OpCodes.Pop.ToInstruction())); // don't add pop before both iterations have finished
|
||||
ternaryBlock.Processed = true;
|
||||
}
|
||||
|
||||
|
||||
public void DeobfuscateBegin(Blocks blocks)
|
||||
{
|
||||
_blocks = blocks;
|
||||
_instructionEmulator.Initialize(_blocks, true);
|
||||
}
|
||||
|
||||
public bool Deobfuscate(List<Block> methodBlocks)
|
||||
{
|
||||
List<Block> switchBlocks = GetSwitchBlocks(methodBlocks); // blocks that contain a switch
|
||||
int modifications = 0;
|
||||
|
||||
foreach (Block switchBlock in switchBlocks)
|
||||
{
|
||||
if (!switchBlock.SwitchData.IsConfuserExSwitch())
|
||||
{
|
||||
Console.WriteLine("Unsupported switch block obfuscation!");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (switchBlock.SwitchData.IsKeyHardCoded)
|
||||
{
|
||||
ProcessHardcodedSwitch(switchBlock);
|
||||
modifications++;
|
||||
continue;
|
||||
}
|
||||
|
||||
_switchKey = Instr.GetLocalVar(_blocks.Locals,
|
||||
switchBlock.Instructions[switchBlock.Instructions.Count - 4]);
|
||||
|
||||
if (DeobfuscateSwitchBlock(methodBlocks, switchBlock))
|
||||
modifications++;
|
||||
}
|
||||
return modifications > 0;
|
||||
}
|
||||
|
||||
private bool DeobfuscateSwitchBlock(List<Block> methodBlocks, Block switchBlock)
|
||||
{
|
||||
List<Block> switchFallThroughs = methodBlocks.FindAll(b => b.FallThrough == switchBlock); // blocks that fallthrough to the switch block
|
||||
_instructionEmulator.Initialize(_blocks, true); //TODO: Remove temporary precaution
|
||||
|
||||
int blocksLeft = switchFallThroughs.Count; // how many blocks left to proccess
|
||||
int blockIndex = 0; // block that sets the first switch destination
|
||||
int failedCount = 0;
|
||||
|
||||
while (blocksLeft > 0)
|
||||
{
|
||||
if (blockIndex > switchFallThroughs.Count - 1)
|
||||
{
|
||||
blockIndex = 0;
|
||||
}
|
||||
|
||||
if (failedCount > switchFallThroughs.Count)
|
||||
{
|
||||
Console.WriteLine("Some blocks couldn't be processed!");
|
||||
break;
|
||||
}
|
||||
|
||||
Block switchCaseBlock = switchFallThroughs[blockIndex];
|
||||
|
||||
if (switchCaseBlock.Processed)
|
||||
{
|
||||
blockIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (NeedSwitchKey(switchCaseBlock))
|
||||
{
|
||||
if (!switchCaseBlock.SwitchData.Key.HasValue)
|
||||
{
|
||||
failedCount++;
|
||||
blockIndex++;
|
||||
continue;
|
||||
}
|
||||
SetLocalSwitchKey(switchCaseBlock.SwitchData.Key.Value);
|
||||
}
|
||||
|
||||
if (switchCaseBlock.IsTernary())
|
||||
{
|
||||
ProcessTernaryBlock(switchFallThroughs, switchCaseBlock, switchBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessBlock(switchFallThroughs, switchCaseBlock, switchBlock);
|
||||
}
|
||||
|
||||
failedCount = 0;
|
||||
blocksLeft--;
|
||||
blockIndex++;
|
||||
}
|
||||
|
||||
if (blocksLeft == switchFallThroughs.Count) // Have we modified anything?
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public bool IsSwitchBlock(Block block)
|
||||
{
|
||||
if (block.LastInstr.OpCode.Code != Code.Switch || ((Instruction[])block.LastInstr.Operand)?.Length == 0)
|
||||
return false;
|
||||
|
||||
if (!block.SwitchData.IsNative())
|
||||
return false;
|
||||
|
||||
_nativeMethod = new X86Method(block.SwitchData.GetNativeMethod(), _blocks.Method.Module as ModuleDefMD); //TODO: Possible null
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<Block> GetSwitchBlocks(List<Block> blocks) // get the blocks which contain the switch statement
|
||||
{
|
||||
List<Block> switchBlocks = new List<Block>();
|
||||
|
||||
foreach (Block block in blocks)
|
||||
if (IsSwitchBlock(block))
|
||||
switchBlocks.Add(block);
|
||||
|
||||
return switchBlocks;
|
||||
}
|
||||
|
||||
|
||||
private readonly List<Block> _processedFallThroughs = new List<Block>();
|
||||
|
||||
// add the switch key to all appropriate fallthroughs
|
||||
private void ProcessFallThroughs(List<Block> switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey)
|
||||
{
|
||||
DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, switchKey);
|
||||
_processedFallThroughs.Clear();
|
||||
}
|
||||
|
||||
private void DoProcessFallThroughs(List<Block> switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey)
|
||||
{
|
||||
if (_processedFallThroughs.Contains(targetBlock))
|
||||
return;
|
||||
|
||||
_processedFallThroughs.Add(targetBlock);
|
||||
|
||||
if (targetBlock.FallThrough == switchBlock && switchCaseBlocks.Contains(targetBlock) && !targetBlock.SwitchData.Key.HasValue)
|
||||
targetBlock.SwitchData.Key = switchKey;
|
||||
|
||||
|
||||
var fallThrough = targetBlock.FallThrough;
|
||||
|
||||
if (fallThrough == null)
|
||||
return;
|
||||
|
||||
if (fallThrough.LastInstr.OpCode != OpCodes.Ret && fallThrough != switchBlock)
|
||||
DoProcessFallThroughs(switchCaseBlocks, switchBlock, fallThrough, switchKey);
|
||||
|
||||
if (targetBlock.CountTargets() > 1)
|
||||
foreach (Block targetBlockTarget in targetBlock.Targets)
|
||||
{
|
||||
if (targetBlockTarget == switchBlock)
|
||||
return;
|
||||
DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlockTarget, switchKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool NeedSwitchKey(Block block)
|
||||
{
|
||||
foreach (var instr in block.Instructions)
|
||||
if (instr.IsLdloc() && Instr.GetLocalVar(_blocks.Locals, instr) == _switchKey)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private int? GetSwitchKey()
|
||||
{
|
||||
var val = _instructionEmulator.GetLocal(_switchKey);
|
||||
if (!val.IsInt32())
|
||||
return null;
|
||||
var value = val as Int32Value;
|
||||
if (value == null || !value.AllBitsValid())
|
||||
return null;
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
private void SetLocalSwitchKey(int key)
|
||||
{
|
||||
_instructionEmulator.SetLocal(_switchKey, new Int32Value(key));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
Copyright (C) 2011-2017 TheProxy
|
||||
|
||||
This file is part of modified 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 de4dot.blocks;
|
||||
using de4dot.blocks.cflow;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace de4dot.code.deobfuscators.ConfuserEx
|
||||
{
|
||||
|
||||
public class DeobfuscatorInfo : DeobfuscatorInfoBase
|
||||
{
|
||||
public const string THE_NAME = "ConfuserEx";
|
||||
public const string THE_TYPE = "cx";
|
||||
const string DEFAULT_REGEX = DeobfuscatorBase.DEFAULT_ASIAN_VALID_NAME_REGEX;
|
||||
|
||||
public DeobfuscatorInfo()
|
||||
: base(DEFAULT_REGEX)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return THE_NAME; }
|
||||
}
|
||||
|
||||
public override string Type
|
||||
{
|
||||
get { return THE_TYPE; }
|
||||
}
|
||||
|
||||
public override IDeobfuscator CreateDeobfuscator()
|
||||
{
|
||||
return new Deobfuscator(new Deobfuscator.Options
|
||||
{
|
||||
RenameResourcesInCode = false,
|
||||
ValidNameRegex = validNameRegex.Get(),
|
||||
});
|
||||
}
|
||||
|
||||
class Deobfuscator : DeobfuscatorBase
|
||||
{
|
||||
|
||||
bool detectedConfuserExAttribute = false, deobfuscating = false;
|
||||
string version = "";
|
||||
LzmaFinder lzmaFinder;
|
||||
ConstantsDecrypter constantDecrypter;
|
||||
ResourceDecrypter resourceDecrypter;
|
||||
|
||||
#region ConstantInliners
|
||||
|
||||
SByteValueInliner sbyteValueInliner;
|
||||
ByteValueInliner byteValueInliner;
|
||||
Int16ValueInliner int16ValueInliner;
|
||||
UInt16ValueInliner uint16ValueInliner;
|
||||
Int32ValueInliner int32ValueInliner;
|
||||
UInt32ValueInliner uint32ValueInliner;
|
||||
Int64ValueInliner int64ValueInliner;
|
||||
UInt64ValueInliner uint64ValueInliner;
|
||||
SingleValueInliner singleValueInliner;
|
||||
DoubleValueInliner doubleValueInliner;
|
||||
ArrayValueInliner arrayValueInliner;
|
||||
|
||||
#endregion
|
||||
|
||||
internal class Options : OptionsBase
|
||||
{
|
||||
}
|
||||
|
||||
public override string Type
|
||||
{
|
||||
get { return DeobfuscatorInfo.THE_TYPE; }
|
||||
}
|
||||
|
||||
public override string TypeLong
|
||||
{
|
||||
get { return DeobfuscatorInfo.THE_NAME; }
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return $"{TypeLong} {version}"; }
|
||||
}
|
||||
|
||||
public Deobfuscator(Options options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override int DetectInternal()
|
||||
{
|
||||
int val = 0;
|
||||
if (detectedConfuserExAttribute) val += 0;
|
||||
if (lzmaFinder.FoundLzma) val += 10;
|
||||
if (constantDecrypter.Detected) val += 10;
|
||||
if (resourceDecrypter.Detected) val += 10;
|
||||
return val;
|
||||
}
|
||||
|
||||
protected override void ScanForObfuscator()
|
||||
{
|
||||
lzmaFinder = new LzmaFinder(module, DeobfuscatedFile);
|
||||
lzmaFinder.Find();
|
||||
constantDecrypter = new ConstantsDecrypter(module, lzmaFinder.Method, DeobfuscatedFile);
|
||||
resourceDecrypter = new ResourceDecrypter(module, lzmaFinder.Method, DeobfuscatedFile);
|
||||
if (lzmaFinder.FoundLzma)
|
||||
{
|
||||
constantDecrypter.Find();
|
||||
resourceDecrypter.Find();
|
||||
}
|
||||
DetectConfuserExAttribute();
|
||||
}
|
||||
|
||||
public void DetectConfuserExAttribute()
|
||||
{
|
||||
var versions = new List<string>();
|
||||
foreach (var attribute in module.CustomAttributes)
|
||||
{
|
||||
if (attribute.TypeFullName != "ConfusedByAttribute")
|
||||
continue;
|
||||
foreach (var argument in attribute.ConstructorArguments)
|
||||
{
|
||||
if (argument.Type.ElementType != ElementType.String)
|
||||
continue;
|
||||
var value = argument.Value.ToString();
|
||||
if (!value.Contains("ConfuserEx"))
|
||||
continue;
|
||||
detectedConfuserExAttribute = true;
|
||||
version = value.Replace("ConfuserEx", "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DeobfuscateBegin()
|
||||
{
|
||||
if (constantDecrypter.Detected)
|
||||
{
|
||||
sbyteValueInliner = new SByteValueInliner();
|
||||
byteValueInliner = new ByteValueInliner();
|
||||
int16ValueInliner = new Int16ValueInliner();
|
||||
uint16ValueInliner = new UInt16ValueInliner();
|
||||
int32ValueInliner = new Int32ValueInliner();
|
||||
uint32ValueInliner = new UInt32ValueInliner();
|
||||
int64ValueInliner = new Int64ValueInliner();
|
||||
uint64ValueInliner = new UInt64ValueInliner();
|
||||
singleValueInliner = new SingleValueInliner();
|
||||
doubleValueInliner = new DoubleValueInliner();
|
||||
arrayValueInliner = new ArrayValueInliner(initializedDataCreator);
|
||||
foreach (var info in constantDecrypter.Decrypters)
|
||||
{
|
||||
staticStringInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptString(info, gim, (uint) args[0]));
|
||||
sbyteValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptSByte(info, gim, (uint) args[0]));
|
||||
byteValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptByte(info, gim, (uint) args[0]));
|
||||
int16ValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptInt16(info, gim, (uint) args[0]));
|
||||
uint16ValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptUInt16(info, gim, (uint) args[0]));
|
||||
int32ValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptInt32(info, gim, (uint) args[0]));
|
||||
uint32ValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptUInt32(info, gim, (uint) args[0]));
|
||||
int64ValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptInt64(info, gim, (uint) args[0]));
|
||||
uint64ValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptUInt64(info, gim, (uint) args[0]));
|
||||
singleValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptSingle(info, gim, (uint) args[0]));
|
||||
doubleValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptDouble(info, gim, (uint) args[0]));
|
||||
arrayValueInliner.Add(info.Method,
|
||||
(method, gim, args) => constantDecrypter.DecryptArray(info, gim, (uint) args[0]));
|
||||
}
|
||||
deobfuscating = true;
|
||||
}
|
||||
if (resourceDecrypter.Detected)
|
||||
resourceDecrypter.Fix();
|
||||
base.DeobfuscateBegin();
|
||||
}
|
||||
|
||||
public override IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators
|
||||
{
|
||||
get
|
||||
{
|
||||
var list = new List<IBlocksDeobfuscator>();
|
||||
list.Add(new ControlFlowSolver());
|
||||
|
||||
if (deobfuscating && int32ValueInliner != null)
|
||||
list.Add(new ConstantsInliner(sbyteValueInliner, byteValueInliner, int16ValueInliner,
|
||||
uint16ValueInliner,
|
||||
int32ValueInliner, uint32ValueInliner, int64ValueInliner, uint64ValueInliner,
|
||||
singleValueInliner, doubleValueInliner, arrayValueInliner)
|
||||
{ExecuteIfNotModified = true});
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
bool CanRemoveLzma = true;
|
||||
|
||||
public override void DeobfuscateEnd()
|
||||
{
|
||||
FindAndRemoveInlinedMethods();
|
||||
|
||||
List<MethodDef> toRemoveFromCctor = new List<MethodDef>();
|
||||
|
||||
if (constantDecrypter.Detected)
|
||||
if (CanRemoveStringDecrypterType)
|
||||
{
|
||||
toRemoveFromCctor.Add(constantDecrypter.Method);
|
||||
AddMethodToBeRemoved(constantDecrypter.Method, "Constant Decrypter Initializer");
|
||||
foreach (var dec in constantDecrypter.Decrypters)
|
||||
AddMethodToBeRemoved(dec.Method, "Constant Decrypter Method");
|
||||
AddFieldsToBeRemoved(constantDecrypter.Fields, "Constant Decrypter Fields");
|
||||
AddTypeToBeRemoved(constantDecrypter.Type, "Array field signature type");
|
||||
}
|
||||
else
|
||||
CanRemoveLzma = false;
|
||||
|
||||
if (resourceDecrypter.Detected && resourceDecrypter.CanRemoveLzma)
|
||||
{
|
||||
toRemoveFromCctor.Add(resourceDecrypter.Method);
|
||||
AddMethodToBeRemoved(resourceDecrypter.Method, "Resource decrypter Initializer method");
|
||||
AddMethodToBeRemoved(resourceDecrypter.AssembyResolveMethod,
|
||||
"Resource decrypter AssemblyResolve method");
|
||||
AddFieldsToBeRemoved(resourceDecrypter.Fields, "Constant Decrypter Fields");
|
||||
AddTypeToBeRemoved(resourceDecrypter.Type, "Array field signature type");
|
||||
}
|
||||
|
||||
if (!constantDecrypter.CanRemoveLzma || !resourceDecrypter.CanRemoveLzma)
|
||||
CanRemoveLzma = false;
|
||||
|
||||
if (lzmaFinder.FoundLzma && CanRemoveLzma)
|
||||
{
|
||||
AddMethodToBeRemoved(lzmaFinder.Method, "Lzma Decompress method");
|
||||
AddTypesToBeRemoved(lzmaFinder.Types, "Lzma Nested Types");
|
||||
}
|
||||
|
||||
var moduleCctor = DotNetUtils.GetModuleTypeCctor(module);
|
||||
foreach (var instr in moduleCctor.Body.Instructions)
|
||||
if (instr.OpCode == OpCodes.Call && instr.Operand is MethodDef &&
|
||||
toRemoveFromCctor.Contains(instr.Operand as MethodDef))
|
||||
instr.OpCode = OpCodes.Nop;
|
||||
|
||||
//No more mixed!
|
||||
module.IsILOnly = true;
|
||||
|
||||
base.DeobfuscateEnd();
|
||||
}
|
||||
|
||||
public override IEnumerable<int> GetStringDecrypterMethods()
|
||||
{
|
||||
var list = new List<int>();
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
using de4dot.blocks;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace de4dot.code.deobfuscators.ConfuserEx
|
||||
{
|
||||
public class LzmaFinder
|
||||
{
|
||||
MethodDef decompressMethod;
|
||||
List<TypeDef> types = new List<TypeDef>();
|
||||
|
||||
public MethodDef Method
|
||||
{
|
||||
get { return decompressMethod; }
|
||||
}
|
||||
public List<TypeDef> Types
|
||||
{
|
||||
get { return types; }
|
||||
}
|
||||
public bool FoundLzma
|
||||
{
|
||||
get { return decompressMethod != null && types.Count != 0; }
|
||||
}
|
||||
|
||||
ModuleDef module;
|
||||
ISimpleDeobfuscator deobfuscator;
|
||||
public LzmaFinder(ModuleDef module, ISimpleDeobfuscator deobfuscator)
|
||||
{
|
||||
this.module = module;
|
||||
this.deobfuscator = deobfuscator;
|
||||
}
|
||||
|
||||
public void Find()
|
||||
{
|
||||
var moduleType = DotNetUtils.GetModuleType(module);
|
||||
if (moduleType == null)
|
||||
return;
|
||||
foreach (var method in moduleType.Methods)
|
||||
{
|
||||
if (!method.HasBody || !method.IsStatic)
|
||||
continue;
|
||||
if (!DotNetUtils.IsMethod(method, "System.Byte[]", "(System.Byte[])"))
|
||||
continue;
|
||||
deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force);
|
||||
if (!IsLzmaMethod(method))
|
||||
continue;
|
||||
decompressMethod = method;
|
||||
var type = ((MethodDef)method.Body.Instructions[3].Operand).DeclaringType;
|
||||
ExtractNestedTypes(type);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsLzmaMethod(MethodDef method)
|
||||
{
|
||||
var instructions = method.Body.Instructions;
|
||||
|
||||
if (instructions.Count < 60)
|
||||
return false;
|
||||
|
||||
var firstInstruction = instructions.FirstOrDefault(
|
||||
instr =>
|
||||
instr.OpCode == OpCodes.Newobj &&
|
||||
instr.Operand.ToString() == "System.Void System.IO.MemoryStream::.ctor(System.Byte[])");
|
||||
|
||||
if (firstInstruction == null)
|
||||
return false;
|
||||
|
||||
int i = instructions.IndexOf(firstInstruction) + 1;
|
||||
|
||||
if (!instructions[i++].IsStloc())
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.Newobj)
|
||||
return false;
|
||||
if (!instructions[i++].IsStloc()) //<Module>.Class1 @class = new <Module>.Class1();
|
||||
return false;
|
||||
|
||||
if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 5)
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.Newarr)
|
||||
return false;
|
||||
if (!instructions[i++].IsStloc()) //byte[] buffer = new byte[5];
|
||||
return false;
|
||||
|
||||
if (!instructions[i++].IsLdloc())
|
||||
return false;
|
||||
if (!instructions[i++].IsLdloc())
|
||||
return false;
|
||||
if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 0)
|
||||
return false;
|
||||
if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 5)
|
||||
return false;
|
||||
if (instructions[i].OpCode != OpCodes.Callvirt || instructions[i++].Operand.ToString() != "System.Int32 System.IO.Stream::Read(System.Byte[],System.Int32,System.Int32)")
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.Pop) //memoryStream.Read(buffer, 0, 5);
|
||||
return false;
|
||||
|
||||
if (!instructions[i++].IsLdloc())
|
||||
return false;
|
||||
if (!instructions[i++].IsLdloc())
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.Callvirt) //@class.method_5(buffer);
|
||||
return false;
|
||||
|
||||
firstInstruction =
|
||||
instructions.FirstOrDefault(
|
||||
instr =>
|
||||
instr.OpCode == OpCodes.Callvirt &&
|
||||
instr.Operand.ToString() == "System.Int32 System.IO.Stream::ReadByte()");
|
||||
|
||||
if (firstInstruction == null)
|
||||
return false;
|
||||
if (i >= instructions.IndexOf(firstInstruction))
|
||||
return false;
|
||||
|
||||
i = instructions.IndexOf(firstInstruction) + 1;
|
||||
|
||||
if (!instructions[i++].IsStloc()) //int num2 = memoryStream.ReadByte();
|
||||
return false;
|
||||
|
||||
if (!instructions[i++].IsLdloc())
|
||||
return false;
|
||||
if (!instructions[i++].IsLdloc())
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.Conv_U1)
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.Conv_U8)
|
||||
return false;
|
||||
if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 8)
|
||||
return false;
|
||||
if (!instructions[i++].IsLdloc())
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.Mul)
|
||||
return false;
|
||||
if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 0x3F)
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.And)
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.Shl)
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.Or)
|
||||
return false;
|
||||
if (!instructions[i++].IsStloc()) //num |= (long)((long)((ulong)((byte)num2)) << 8 * i);
|
||||
return false;
|
||||
|
||||
firstInstruction =
|
||||
instructions.FirstOrDefault(
|
||||
instr =>
|
||||
instr.OpCode == OpCodes.Newobj &&
|
||||
instr.Operand.ToString() == "System.Void System.IO.MemoryStream::.ctor(System.Byte[],System.Boolean)");
|
||||
|
||||
if (firstInstruction == null)
|
||||
return false;
|
||||
if (i >= instructions.IndexOf(firstInstruction))
|
||||
return false;
|
||||
|
||||
i = instructions.IndexOf(firstInstruction) + 1;
|
||||
|
||||
if (!instructions[i++].IsStloc()) //MemoryStream stream_ = new MemoryStream(array, true);
|
||||
return false;
|
||||
|
||||
if (!instructions[i++].IsLdloc())
|
||||
return false;
|
||||
if (instructions[i].OpCode != OpCodes.Callvirt || instructions[i++].Operand.ToString() != "System.Int64 System.IO.Stream::get_Length()")
|
||||
return false;
|
||||
if (instructions[i].OpCode != OpCodes.Ldc_I8 || (long)instructions[i++].Operand != 13L)
|
||||
return false;
|
||||
if (instructions[i++].OpCode != OpCodes.Sub)
|
||||
return false;
|
||||
if (!instructions[i++].IsStloc()) //long long_ = memoryStream.Length - 13L;
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
private void ExtractNestedTypes(TypeDef type)
|
||||
{
|
||||
foreach (var method in type.Methods)
|
||||
if (method.HasBody)
|
||||
{
|
||||
var instr = method.Body.Instructions;
|
||||
foreach (var inst in instr)
|
||||
if (inst.Operand is MethodDef)
|
||||
{
|
||||
var ntype = (inst.Operand as MethodDef).DeclaringType;
|
||||
if (!ntype.IsNested)
|
||||
continue;
|
||||
if (types.Contains(ntype))
|
||||
continue;
|
||||
types.Add(ntype);
|
||||
ExtractNestedTypes(ntype);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,293 @@
|
|||
using de4dot.blocks;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace de4dot.code.deobfuscators.ConfuserEx
|
||||
{
|
||||
public class ResourceDecrypter
|
||||
{
|
||||
TypeDef arrayType;
|
||||
MethodDef resourceDecInitMethod, assembyResolveMethod;
|
||||
FieldDef arrayField, asmField;
|
||||
byte[] decryptedBytes;
|
||||
|
||||
bool canRemoveLzma = true;
|
||||
|
||||
public bool CanRemoveLzma
|
||||
{
|
||||
get { return canRemoveLzma; }
|
||||
}
|
||||
|
||||
public TypeDef Type
|
||||
{
|
||||
get { return arrayType; }
|
||||
}
|
||||
public MethodDef Method
|
||||
{
|
||||
get { return resourceDecInitMethod; }
|
||||
}
|
||||
public MethodDef AssembyResolveMethod
|
||||
{
|
||||
get { return assembyResolveMethod; }
|
||||
}
|
||||
public List<FieldDef> Fields
|
||||
{
|
||||
get { return new List<FieldDef>() { arrayField, asmField }; }
|
||||
}
|
||||
public bool Detected
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceDecInitMethod != null && resourceDecInitMethod != null
|
||||
&& decryptedBytes != null && arrayField != null && asmField != null;
|
||||
}
|
||||
}
|
||||
|
||||
ModuleDef module;
|
||||
MethodDef lzmaMethod;
|
||||
ISimpleDeobfuscator deobfuscator;
|
||||
public ResourceDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfuscator deobfsucator)
|
||||
{
|
||||
this.module = module;
|
||||
this.lzmaMethod = lzmaMethod;
|
||||
this.deobfuscator = deobfsucator;
|
||||
}
|
||||
|
||||
public void Find()
|
||||
{
|
||||
var moduleCctor = DotNetUtils.GetModuleTypeCctor(module);
|
||||
if (moduleCctor == null)
|
||||
return;
|
||||
foreach (var inst in moduleCctor.Body.Instructions)
|
||||
{
|
||||
if (inst.OpCode != OpCodes.Call)
|
||||
continue;
|
||||
if (!(inst.Operand is MethodDef))
|
||||
continue;
|
||||
var method = inst.Operand as MethodDef;
|
||||
if (!method.HasBody || !method.IsStatic)
|
||||
continue;
|
||||
if (!DotNetUtils.IsMethod(method, "System.Void", "()"))
|
||||
continue;
|
||||
deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force);
|
||||
if (!isResDecryptInit(method))
|
||||
continue;
|
||||
resourceDecInitMethod = method;
|
||||
}
|
||||
}
|
||||
|
||||
bool isResDecryptInit(MethodDef method)
|
||||
{
|
||||
var instr = method.Body.Instructions;
|
||||
|
||||
if (instr.Count < 15)
|
||||
return false;
|
||||
|
||||
if (!instr[0].IsLdcI4())
|
||||
return false;
|
||||
if (!instr[1].IsStloc()) //uint num = 96u;
|
||||
return false;
|
||||
|
||||
if (!instr[2].IsLdcI4())
|
||||
return false;
|
||||
if (instr[0].GetLdcI4Value() != instr[2].GetLdcI4Value())
|
||||
return false;
|
||||
if (instr[3].OpCode != OpCodes.Newarr)
|
||||
return false;
|
||||
if (instr[3].Operand.ToString() != "System.UInt32")
|
||||
return false;
|
||||
if (instr[4].OpCode != OpCodes.Dup)
|
||||
return false;
|
||||
if (instr[5].OpCode != OpCodes.Ldtoken)
|
||||
return false;
|
||||
var aField = instr[5].Operand as FieldDef;
|
||||
if (aField == null)
|
||||
return false;
|
||||
if (aField.InitialValue == null)
|
||||
return false;
|
||||
if (aField.Attributes != (FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.HasFieldRVA))
|
||||
return false;
|
||||
if (instr[6].OpCode != OpCodes.Call)
|
||||
return false;
|
||||
if (instr[6].Operand.ToString() != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)")
|
||||
return false;
|
||||
if (!instr[7].IsStloc()) // uint[] array = new uint[] {.....};
|
||||
return false;
|
||||
|
||||
var l = instr.Count;
|
||||
if (!instr[l - 10].IsLdloc())
|
||||
return false;
|
||||
if (instr[l - 9].OpCode != OpCodes.Call)
|
||||
return false;
|
||||
if (instr[l - 9].Operand != lzmaMethod)
|
||||
return false;
|
||||
if (instr[l - 8].OpCode != OpCodes.Call)
|
||||
return false;
|
||||
if (instr[l - 8].Operand.ToString() != "System.Reflection.Assembly System.Reflection.Assembly::Load(System.Byte[])")
|
||||
return false;
|
||||
if (instr[l - 7].OpCode != OpCodes.Stsfld) //<Module>.assembly_0 = Assembly.Load(array4);
|
||||
return false;
|
||||
var asField = instr[l - 7].Operand as FieldDef;
|
||||
if (asField == null)
|
||||
return false;
|
||||
|
||||
if (instr[l - 6].OpCode != OpCodes.Call)
|
||||
return false;
|
||||
if (instr[l - 6].Operand.ToString() != "System.AppDomain System.AppDomain::get_CurrentDomain()")
|
||||
return false;
|
||||
if (instr[l - 5].OpCode != OpCodes.Ldnull)
|
||||
return false;
|
||||
if (instr[l - 4].OpCode != OpCodes.Ldftn)
|
||||
return false;
|
||||
var mtd = instr[l - 4].Operand as MethodDef;
|
||||
if (mtd == null)
|
||||
return false;
|
||||
if (!IsAssembyResolveMethod(mtd, asField))
|
||||
return false;
|
||||
if (instr[l - 3].OpCode != OpCodes.Newobj)
|
||||
return false;
|
||||
if (instr[l - 2].OpCode != OpCodes.Callvirt) //AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(<Module>.smethod_1);
|
||||
return false;
|
||||
try
|
||||
{
|
||||
DecryptArray(ConvertArray<uint, byte>(aField.InitialValue));
|
||||
}
|
||||
catch { canRemoveLzma = false; return false; }
|
||||
arrayField = aField;
|
||||
arrayType = DotNetUtils.GetType(module, aField.FieldSig.Type);
|
||||
asmField = asField;
|
||||
assembyResolveMethod = mtd;
|
||||
return true;
|
||||
}
|
||||
private T[] ConvertArray<T, T1>(T1[] array)
|
||||
{
|
||||
int l = Marshal.SizeOf(typeof(T));
|
||||
int l1 = Marshal.SizeOf(typeof(T1));
|
||||
var buffer = new T[(array.Length * l1) / l];
|
||||
Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1);
|
||||
return buffer;
|
||||
}
|
||||
private void DecryptArray(uint[] array)
|
||||
{
|
||||
int num = array.Length;
|
||||
uint[] array2 = new uint[16];
|
||||
uint num2 = 825993394u;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
num2 ^= num2 >> 13;
|
||||
num2 ^= num2 << 25;
|
||||
num2 ^= num2 >> 27;
|
||||
array2[i] = num2;
|
||||
}
|
||||
int num3 = 0;
|
||||
int num4 = 0;
|
||||
uint[] array3 = new uint[16];
|
||||
byte[] array4 = new byte[num * 4u];
|
||||
while ((long)num3 < (long)((ulong)num))
|
||||
{
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
array3[j] = array[num3 + j];
|
||||
}
|
||||
array3[0] = (array3[0] ^ array2[0]);
|
||||
array3[1] = (array3[1] ^ array2[1]);
|
||||
array3[2] = (array3[2] ^ array2[2]);
|
||||
array3[3] = (array3[3] ^ array2[3]);
|
||||
array3[4] = (array3[4] ^ array2[4]);
|
||||
array3[5] = (array3[5] ^ array2[5]);
|
||||
array3[6] = (array3[6] ^ array2[6]);
|
||||
array3[7] = (array3[7] ^ array2[7]);
|
||||
array3[8] = (array3[8] ^ array2[8]);
|
||||
array3[9] = (array3[9] ^ array2[9]);
|
||||
array3[10] = (array3[10] ^ array2[10]);
|
||||
array3[11] = (array3[11] ^ array2[11]);
|
||||
array3[12] = (array3[12] ^ array2[12]);
|
||||
array3[13] = (array3[13] ^ array2[13]);
|
||||
array3[14] = (array3[14] ^ array2[14]);
|
||||
array3[15] = (array3[15] ^ array2[15]);
|
||||
for (int k = 0; k < 16; k++)
|
||||
{
|
||||
uint num5 = array3[k];
|
||||
array4[num4++] = (byte)num5;
|
||||
array4[num4++] = (byte)(num5 >> 8);
|
||||
array4[num4++] = (byte)(num5 >> 16);
|
||||
array4[num4++] = (byte)(num5 >> 24);
|
||||
array2[k] ^= num5;
|
||||
}
|
||||
num3 += 16;
|
||||
}
|
||||
decryptedBytes = Lzma.Decompress(array4);
|
||||
}
|
||||
|
||||
private bool IsAssembyResolveMethod(MethodDef method, FieldDef field)
|
||||
{
|
||||
if (DotNetUtils.IsMethod(method, "", "()"))
|
||||
return false;
|
||||
deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force);
|
||||
|
||||
var instr = method.Body.Instructions;
|
||||
if (instr.Count != 10)
|
||||
return false;
|
||||
|
||||
if (instr[0].OpCode != OpCodes.Ldsfld)
|
||||
return false;
|
||||
if (instr[0].Operand != field)
|
||||
return false;
|
||||
if (instr[1].OpCode != OpCodes.Callvirt)
|
||||
return false;
|
||||
if (instr[1].Operand.ToString() != "System.String System.Reflection.Assembly::get_FullName()")
|
||||
return false;
|
||||
if (!instr[2].IsLdarg())
|
||||
return false;
|
||||
if (instr[3].OpCode != OpCodes.Callvirt)
|
||||
return false;
|
||||
if (instr[3].Operand.ToString() != "System.String System.ResolveEventArgs::get_Name()")
|
||||
return false;
|
||||
if (instr[4].OpCode != OpCodes.Call)
|
||||
return false;
|
||||
if (instr[4].Operand.ToString() != "System.Boolean System.String::op_Equality(System.String,System.String)")
|
||||
return false;
|
||||
if (!instr[5].IsBrfalse())
|
||||
return false;
|
||||
if (instr[6].OpCode != OpCodes.Ldsfld)
|
||||
return false;
|
||||
if (instr[6].Operand != field)
|
||||
return false;
|
||||
if (instr[7].OpCode != OpCodes.Ret)
|
||||
return false;
|
||||
if (instr[8].OpCode != OpCodes.Ldnull)
|
||||
return false;
|
||||
if (instr[9].OpCode != OpCodes.Ret)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Fix()
|
||||
{
|
||||
ModuleDef newModule;
|
||||
try
|
||||
{
|
||||
newModule = ModuleDefMD.Load(decryptedBytes);
|
||||
}
|
||||
catch { canRemoveLzma = false; return; }
|
||||
List<Resource> toRemove = new List<Resource>();
|
||||
List<Resource> toAdd = new List<Resource>();
|
||||
foreach (var cryptedResource in module.Resources)
|
||||
foreach (var resource in newModule.Resources)
|
||||
if (cryptedResource.Name == resource.Name)
|
||||
{
|
||||
toRemove.Add(cryptedResource);
|
||||
toAdd.Add(resource);
|
||||
}
|
||||
|
||||
foreach (var resToRemove in toRemove)
|
||||
module.Resources.Remove(resToRemove);
|
||||
foreach (var resToAdd in toAdd)
|
||||
module.Resources.Add(resToAdd);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using de4dot.blocks;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.code.deobfuscators.ConfuserEx
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
public static bool IsArithmetical(this Instr instr)
|
||||
{
|
||||
switch (instr.OpCode.Code)
|
||||
{
|
||||
case Code.Add:
|
||||
case Code.Add_Ovf:
|
||||
case Code.Add_Ovf_Un:
|
||||
case Code.Div:
|
||||
case Code.Div_Un:
|
||||
case Code.Mul:
|
||||
case Code.Mul_Ovf:
|
||||
case Code.Mul_Ovf_Un:
|
||||
case Code.Not:
|
||||
case Code.Shl:
|
||||
case Code.Shr:
|
||||
case Code.Shr_Un:
|
||||
case Code.Sub:
|
||||
case Code.Sub_Ovf:
|
||||
case Code.Sub_Ovf_Un:
|
||||
case Code.Xor:
|
||||
case Code.And:
|
||||
case Code.Rem:
|
||||
case Code.Rem_Un:
|
||||
case Code.Ceq:
|
||||
case Code.Cgt:
|
||||
case Code.Cgt_Un:
|
||||
case Code.Clt:
|
||||
case Code.Clt_Un:
|
||||
case Code.Neg:
|
||||
case Code.Or:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsConv(this Instr instr)
|
||||
{
|
||||
switch (instr.OpCode.Code)
|
||||
{
|
||||
case Code.Conv_I1:
|
||||
case Code.Conv_I2:
|
||||
case Code.Conv_I4:
|
||||
case Code.Conv_I8:
|
||||
case Code.Conv_U1:
|
||||
case Code.Conv_U2:
|
||||
case Code.Conv_U4:
|
||||
case Code.Conv_U8:
|
||||
case Code.Conv_R4:
|
||||
case Code.Conv_R8:
|
||||
case Code.Conv_Ovf_I1:
|
||||
case Code.Conv_Ovf_I1_Un:
|
||||
case Code.Conv_Ovf_I2:
|
||||
case Code.Conv_Ovf_I2_Un:
|
||||
case Code.Conv_Ovf_I4:
|
||||
case Code.Conv_Ovf_I4_Un:
|
||||
case Code.Conv_Ovf_I8:
|
||||
case Code.Conv_Ovf_I8_Un:
|
||||
case Code.Conv_Ovf_U1:
|
||||
case Code.Conv_Ovf_U1_Un:
|
||||
case Code.Conv_Ovf_U2:
|
||||
case Code.Conv_Ovf_U2_Un:
|
||||
case Code.Conv_Ovf_U4:
|
||||
case Code.Conv_Ovf_U4_Un:
|
||||
case Code.Conv_Ovf_U8:
|
||||
case Code.Conv_Ovf_U8_Un:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsLdc(this Instr instr)
|
||||
{
|
||||
switch (instr.OpCode.Code)
|
||||
{
|
||||
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:
|
||||
case Code.Ldc_I8:
|
||||
case Code.Ldc_R4:
|
||||
case Code.Ldc_R8:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsLoc(this Instr instr)
|
||||
{
|
||||
switch (instr.OpCode.Code)
|
||||
{
|
||||
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.Ldloca:
|
||||
case Code.Ldloca_S:
|
||||
case Code.Stloc:
|
||||
case Code.Stloc_S:
|
||||
case Code.Stloc_0:
|
||||
case Code.Stloc_1:
|
||||
case Code.Stloc_2:
|
||||
case Code.Stloc_3:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsValidInstr(this Instr instr)
|
||||
{
|
||||
return IsArithmetical(instr) || instr.IsConv() || IsLdc(instr) || IsLoc(instr) || instr.OpCode == OpCodes.Dup;
|
||||
}
|
||||
|
||||
public static bool IsDup(this Block block)
|
||||
{
|
||||
if (block.Sources.Count != 1)
|
||||
return false;
|
||||
if (block.Instructions.Count != 2)
|
||||
return false;
|
||||
if (!block.FirstInstr.IsLdcI4())
|
||||
return false;
|
||||
if (block.LastInstr.OpCode != OpCodes.Dup)
|
||||
if (!block.LastInstr.IsLdcI4() || block.LastInstr.GetLdcI4Value() != block.FirstInstr.GetLdcI4Value())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static bool IsConfuserExSwitch(this SwitchData switchData)
|
||||
{
|
||||
var instructions = switchData.Block.Instructions;
|
||||
var lastIndex = instructions.Count - 1;
|
||||
|
||||
if (instructions.Count < 4)
|
||||
return false;
|
||||
if (!instructions[lastIndex - 3].IsStloc())
|
||||
return false;
|
||||
if (!instructions[lastIndex - 2].IsLdcI4())
|
||||
return false;
|
||||
if (instructions[lastIndex - 1].OpCode != OpCodes.Rem_Un)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsNative(this SwitchData switchData)
|
||||
{
|
||||
var block = switchData.Block;
|
||||
|
||||
var instr = block.Instructions;
|
||||
if (instr.Count <= 4)
|
||||
return false;
|
||||
|
||||
if (instr[0].IsLdcI4() && instr[1].OpCode == OpCodes.Call)
|
||||
{
|
||||
switchData.IsKeyHardCoded = true;
|
||||
block.SwitchData.Key = block.FirstInstr.GetLdcI4Value();
|
||||
}
|
||||
|
||||
if (!switchData.IsKeyHardCoded && instr[0].OpCode != OpCodes.Call)
|
||||
return false;
|
||||
|
||||
var method = block.Instructions[switchData.IsKeyHardCoded ? 1 : 0].Operand as MethodDef;
|
||||
|
||||
if (method == null || !method.IsStatic || !method.IsNative)
|
||||
return false;
|
||||
if (!DotNetUtils.IsMethod(method, "System.Int32", "(System.Int32)"))
|
||||
return false;
|
||||
for (int i = switchData.IsKeyHardCoded ? 2 : 1; i < instr.Count - 1; i++)
|
||||
if (!instr[i].IsValidInstr())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static MethodDef GetNativeMethod(this SwitchData switchData)
|
||||
{
|
||||
var block = switchData.Block;
|
||||
|
||||
var method = block.Instructions[switchData.IsKeyHardCoded ? 1 : 0].Operand as MethodDef;
|
||||
return method;
|
||||
}
|
||||
|
||||
public static bool IsTernaryPredicate(this Block ternaryPredicateBlock)
|
||||
{
|
||||
if (!ternaryPredicateBlock.LastInstr.IsConditionalBranch())
|
||||
return false;
|
||||
|
||||
if (ternaryPredicateBlock.CountTargets() > 2)
|
||||
return false;
|
||||
|
||||
var source1 = ternaryPredicateBlock.Targets[0];
|
||||
var source2 = ternaryPredicateBlock.FallThrough;
|
||||
|
||||
//if (!IsDup(source1) || !IsDup(source2))
|
||||
// return false;
|
||||
|
||||
if (source1.CountTargets() > 1 || source2.CountTargets() > 1)
|
||||
return false;
|
||||
|
||||
var mainBlock = source1.FallThrough;
|
||||
|
||||
if (mainBlock != source2.FallThrough)
|
||||
return false;
|
||||
|
||||
if (mainBlock.Sources.Count != 2)
|
||||
return false;
|
||||
if (mainBlock.LastInstr.OpCode == OpCodes.Ret)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsTernary(this Block block)
|
||||
{
|
||||
var sources = block.Sources;
|
||||
if (sources.Count != 2)
|
||||
return false;
|
||||
if (!sources[0].IsDup() || !(sources[1]).IsDup()) //TODO: Case without DUP?
|
||||
return false;
|
||||
if (sources[0].CountTargets() > 1 || sources[1].CountTargets() > 1)
|
||||
return false;
|
||||
if (sources[0].FallThrough != block || sources[1].FallThrough != block)
|
||||
return false;
|
||||
if (sources[0].Sources[0] != sources[1].Sources[0])
|
||||
return false;
|
||||
if (!sources[0].Sources[0].IsConditionalBranch())
|
||||
return false;
|
||||
if (block.LastInstr.OpCode == OpCodes.Ret)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<Block> GetTernaryPredicates(this List<Block> switchCaseBlocks)
|
||||
{
|
||||
List<Block> ternaryPredicates = new List<Block>();
|
||||
|
||||
foreach (Block preBlock in switchCaseBlocks)
|
||||
{
|
||||
if (IsTernary(preBlock))
|
||||
{
|
||||
// switchCaseBlock -> 2x sourceBlock -> ternaryPredicateBlock
|
||||
ternaryPredicates.Add(preBlock.Sources[0].Sources[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return ternaryPredicates;
|
||||
}
|
||||
|
||||
public static Block GetTernaryPredicateMainBlock(this Block ternaryPredicateBlock)
|
||||
{
|
||||
return ternaryPredicateBlock.FallThrough.FallThrough;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
|
||||
namespace de4dot.Bea
|
||||
{
|
||||
public class BeaConstants
|
||||
{
|
||||
public static int INSTRUCT_LENGTH = 64;
|
||||
|
||||
public enum SegmentRegister : byte
|
||||
{
|
||||
ESReg = 1,
|
||||
DSReg = 2,
|
||||
FSReg = 3,
|
||||
GSReg = 4,
|
||||
CSReg = 5,
|
||||
SSReg = 6
|
||||
}
|
||||
|
||||
public enum PrefixType : byte
|
||||
{
|
||||
NotUsedPrefix = 0,
|
||||
InUsePrefix = 1,
|
||||
SuperfluousPrefix = 2,
|
||||
InvalidPrefix = 4,
|
||||
MandatoryPrefix = 8
|
||||
}
|
||||
|
||||
public enum InstructionType : uint
|
||||
{
|
||||
GENERAL_PURPOSE_INSTRUCTION = 0x10000,
|
||||
FPU_INSTRUCTION = 0x20000,
|
||||
MMX_INSTRUCTION = 0x40000,
|
||||
SSE_INSTRUCTION = 0x80000,
|
||||
SSE2_INSTRUCTION = 0x100000,
|
||||
SSE3_INSTRUCTION = 0x200000,
|
||||
SSSE3_INSTRUCTION = 0x400000,
|
||||
SSE41_INSTRUCTION = 0x800000,
|
||||
SSE42_INSTRUCTION = 0x1000000,
|
||||
SYSTEM_INSTRUCTION = 0x2000000,
|
||||
VM_INSTRUCTION = 0x4000000,
|
||||
UNDOCUMENTED_INSTRUCTION = 0x8000000,
|
||||
AMD_INSTRUCTION = 0x10000000,
|
||||
ILLEGAL_INSTRUCTION = 0x20000000,
|
||||
AES_INSTRUCTION = 0x40000000,
|
||||
CLMUL_INSTRUCTION = 0x80000000,
|
||||
|
||||
DATA_TRANSFER = 0x1,
|
||||
ARITHMETIC_INSTRUCTION,
|
||||
LOGICAL_INSTRUCTION,
|
||||
SHIFT_ROTATE,
|
||||
BIT_UInt8,
|
||||
CONTROL_TRANSFER,
|
||||
STRING_INSTRUCTION,
|
||||
InOutINSTRUCTION,
|
||||
ENTER_LEAVE_INSTRUCTION,
|
||||
FLAG_CONTROL_INSTRUCTION,
|
||||
SEGMENT_REGISTER,
|
||||
MISCELLANEOUS_INSTRUCTION,
|
||||
COMPARISON_INSTRUCTION,
|
||||
LOGARITHMIC_INSTRUCTION,
|
||||
TRIGONOMETRIC_INSTRUCTION,
|
||||
UNSUPPORTED_INSTRUCTION,
|
||||
LOAD_CONSTANTS,
|
||||
FPUCONTROL,
|
||||
STATE_MANAGEMENT,
|
||||
CONVERSION_INSTRUCTION,
|
||||
SHUFFLE_UNPACK,
|
||||
PACKED_SINGLE_PRECISION,
|
||||
SIMD128bits,
|
||||
SIMD64bits,
|
||||
CACHEABILITY_CONTROL,
|
||||
FP_INTEGER_CONVERSION,
|
||||
SPECIALIZED_128bits,
|
||||
SIMD_FP_PACKED,
|
||||
SIMD_FP_HORIZONTAL,
|
||||
AGENT_SYNCHRONISATION,
|
||||
PACKED_ALIGN_RIGHT,
|
||||
PACKED_SIGN,
|
||||
PACKED_BLENDING_INSTRUCTION,
|
||||
PACKED_TEST,
|
||||
PACKED_MINMAX,
|
||||
HORIZONTAL_SEARCH,
|
||||
PACKED_EQUALITY,
|
||||
STREAMING_LOAD,
|
||||
INSERTION_EXTRACTION,
|
||||
DOT_PRODUCT,
|
||||
SAD_INSTRUCTION,
|
||||
ACCELERATOR_INSTRUCTION,
|
||||
ROUND_INSTRUCTION
|
||||
}
|
||||
|
||||
public enum EFlagState : byte
|
||||
{
|
||||
TE_ = 1,
|
||||
MO_ = 2,
|
||||
RE_ = 4,
|
||||
SE_ = 8,
|
||||
UN_ = 0x10,
|
||||
PR_ = 0x20
|
||||
}
|
||||
|
||||
public enum BranchType : short
|
||||
{
|
||||
JO = 1,
|
||||
JC,
|
||||
JE,
|
||||
JA,
|
||||
JS,
|
||||
JP,
|
||||
JL,
|
||||
JG,
|
||||
JB,
|
||||
JECXZ,
|
||||
JmpType,
|
||||
CallType,
|
||||
RetType,
|
||||
JNO = -1,
|
||||
JNC = -2,
|
||||
JNE = -3,
|
||||
JNA = -4,
|
||||
JNS = -5,
|
||||
JNP = -6,
|
||||
JNL = -7,
|
||||
JNG = -8,
|
||||
JNB = -9
|
||||
}
|
||||
|
||||
public enum ArgumentType : uint
|
||||
{
|
||||
NO_ARGUMENT = 0x10000000,
|
||||
REGISTER_TYPE = 0x20000000,
|
||||
MEMORY_TYPE = 0x40000000,
|
||||
CONSTANT_TYPE = 0x80000000,
|
||||
|
||||
MMX_REG = 0x10000,
|
||||
GENERAL_REG = 0x20000,
|
||||
FPU_REG = 0x40000,
|
||||
SSE_REG = 0x80000,
|
||||
CR_REG = 0x100000,
|
||||
DR_REG = 0x200000,
|
||||
SPECIAL_REG = 0x400000,
|
||||
MEMORY_MANAGEMENT_REG = 0x800000,
|
||||
SEGMENT_REG = 0x1000000,
|
||||
|
||||
RELATIVE_ = 0x4000000,
|
||||
ABSOLUTE_ = 0x8000000,
|
||||
|
||||
READ = 0x1,
|
||||
WRITE = 0x2,
|
||||
|
||||
REG0 = 0x1,
|
||||
REG1 = 0x2,
|
||||
REG2 = 0x4,
|
||||
REG3 = 0x8,
|
||||
REG4 = 0x10,
|
||||
REG5 = 0x20,
|
||||
REG6 = 0x40,
|
||||
REG7 = 0x80,
|
||||
REG8 = 0x100,
|
||||
REG9 = 0x200,
|
||||
REG10 = 0x400,
|
||||
REG11 = 0x800,
|
||||
REG12 = 0x1000,
|
||||
REG13 = 0x2000,
|
||||
REG14 = 0x4000,
|
||||
REG15 = 0x8000
|
||||
}
|
||||
|
||||
public enum SpecialInfo : int
|
||||
{
|
||||
UNKNOWN_OPCODE = -1,
|
||||
OUT_OF_BLOCK = 0,
|
||||
|
||||
/* === mask = 0xff */
|
||||
NoTabulation = 0x00000000,
|
||||
Tabulation = 0x00000001,
|
||||
|
||||
/* === mask = 0xff00 */
|
||||
MasmSyntax = 0x00000000,
|
||||
GoAsmSyntax = 0x00000100,
|
||||
NasmSyntax = 0x00000200,
|
||||
ATSyntax = 0x00000400,
|
||||
|
||||
/* === mask = 0xff0000 */
|
||||
PrefixedNumeral = 0x00010000,
|
||||
SuffixedNumeral = 0x00000000,
|
||||
|
||||
/* === mask = 0xff000000 */
|
||||
ShowSegmentRegs = 0x01000000
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace de4dot.Bea
|
||||
{
|
||||
public class BeaEngine
|
||||
{
|
||||
[DllImport("BeaEngine.dll")]
|
||||
public static extern int Disasm([In, Out, MarshalAs(UnmanagedType.LPStruct)] Disasm disasm);
|
||||
|
||||
[DllImport("BeaEngine.dll")]
|
||||
private static extern string BeaEngineVersion();
|
||||
|
||||
[DllImport("BeaEngine.dll")]
|
||||
private static extern string BeaEngineRevision();
|
||||
|
||||
public static string Version
|
||||
{
|
||||
get
|
||||
{
|
||||
return BeaEngineVersion();
|
||||
}
|
||||
}
|
||||
|
||||
public static string Revision
|
||||
{
|
||||
get
|
||||
{
|
||||
return BeaEngineRevision();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace de4dot.Bea
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public class REX_Struct
|
||||
{
|
||||
public byte W_;
|
||||
public byte R_;
|
||||
public byte X_;
|
||||
public byte B_;
|
||||
public byte state;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public class PrefixInfo
|
||||
{
|
||||
public int Number;
|
||||
public int NbUndefined;
|
||||
public byte LockPrefix;
|
||||
public byte OperandSize;
|
||||
public byte AddressSize;
|
||||
public byte RepnePrefix;
|
||||
public byte RepPrefix;
|
||||
public byte FSPrefix;
|
||||
public byte SSPrefix;
|
||||
public byte GSPrefix;
|
||||
public byte ESPrefix;
|
||||
public byte CSPrefix;
|
||||
public byte DSPrefix;
|
||||
public byte BranchTaken;
|
||||
public byte BranchNotTaken;
|
||||
public REX_Struct REX;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public class EFLStruct
|
||||
{
|
||||
public byte OF_;
|
||||
public byte SF_;
|
||||
public byte ZF_;
|
||||
public byte AF_;
|
||||
public byte PF_;
|
||||
public byte CF_;
|
||||
public byte TF_;
|
||||
public byte IF_;
|
||||
public byte DF_;
|
||||
public byte NT_;
|
||||
public byte RF_;
|
||||
public byte alignment;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public class MemoryType
|
||||
{
|
||||
public Int32 BaseRegister;
|
||||
public Int32 IndexRegister;
|
||||
public Int32 Scale;
|
||||
public Int64 Displacement;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public class InstructionType
|
||||
{
|
||||
public Int32 Category;
|
||||
public Int32 Opcode;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
|
||||
public string Mnemonic;
|
||||
public Int32 BranchType;
|
||||
public EFLStruct Flags;
|
||||
public UInt64 AddrValue;
|
||||
public Int64 Immediat;
|
||||
public UInt32 ImplicitModifiedRegs;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public class ArgumentType
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
||||
public string ArgMnemonic;
|
||||
public Int32 ArgType;
|
||||
public Int32 ArgSize;
|
||||
public UInt32 AccessMode;
|
||||
public MemoryType Memory;
|
||||
public UInt32 SegmentReg;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public class Disasm
|
||||
{
|
||||
public IntPtr EIP;
|
||||
public UInt64 VirtualAddr;
|
||||
public UInt32 SecurityBlock;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
|
||||
public string CompleteInstr;
|
||||
public UInt32 Archi;
|
||||
public UInt64 Options;
|
||||
public InstructionType Instruction;
|
||||
public ArgumentType Argument1;
|
||||
public ArgumentType Argument2;
|
||||
public ArgumentType Argument3;
|
||||
public PrefixInfo Prefix;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 40, ArraySubType = UnmanagedType.U4)]
|
||||
UInt32[] Reserved_;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.Bea;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
|
||||
{
|
||||
class X86ADD : X86Instruction
|
||||
{
|
||||
public X86ADD(Disasm rawInstruction) : base()
|
||||
{
|
||||
Operands = new IX86Operand[2];
|
||||
Operands[0] = GetOperand(rawInstruction.Argument1);
|
||||
Operands[1] = GetOperand(rawInstruction.Argument2);
|
||||
}
|
||||
|
||||
public override X86OpCode OpCode { get { return X86OpCode.ADD; } }
|
||||
|
||||
public override void Execute(Dictionary<string, int> registers, Stack<int> localStack)
|
||||
{
|
||||
if (Operands[1] is X86ImmediateOperand)
|
||||
registers[((X86RegisterOperand) Operands[0]).Register.ToString()] +=
|
||||
((X86ImmediateOperand) Operands[1]).Immediate;
|
||||
else
|
||||
registers[((X86RegisterOperand) Operands[0]).Register.ToString()] +=
|
||||
registers[((X86RegisterOperand) Operands[1]).Register.ToString()];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.Bea;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
|
||||
{
|
||||
class X86DIV : X86Instruction
|
||||
{
|
||||
public X86DIV(Disasm rawInstruction) : base()
|
||||
{
|
||||
Operands = new IX86Operand[2];
|
||||
Operands[0] = GetOperand(rawInstruction.Argument1);
|
||||
Operands[1] = GetOperand(rawInstruction.Argument2);
|
||||
}
|
||||
|
||||
public override X86OpCode OpCode { get { return X86OpCode.DIV; } }
|
||||
|
||||
public override void Execute(Dictionary<string, int> registers, Stack<int> localStack)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.Bea;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
|
||||
{
|
||||
class X86IMUL : X86Instruction
|
||||
{
|
||||
public X86IMUL(Disasm rawInstruction) : base()
|
||||
{
|
||||
Operands = new IX86Operand[3];
|
||||
Operands[0] = GetOperand(rawInstruction.Argument1);
|
||||
Operands[1] =GetOperand( rawInstruction.Argument2);
|
||||
Operands[2] = GetOperand(rawInstruction.Argument3);
|
||||
}
|
||||
|
||||
public override X86OpCode OpCode { get { return X86OpCode.IMUL; } }
|
||||
|
||||
public override void Execute(Dictionary<string, int> registers, Stack<int> localStack)
|
||||
{
|
||||
var source = ((X86RegisterOperand) Operands[0]).Register.ToString();
|
||||
var target1 = ((X86RegisterOperand) Operands[1]).Register.ToString();
|
||||
|
||||
if (Operands[2] is X86ImmediateOperand)
|
||||
registers[source] = registers[target1]*((X86ImmediateOperand) Operands[2]).Immediate;
|
||||
else
|
||||
registers[source] = registers[target1]*registers[((X86RegisterOperand) Operands[2]).Register.ToString()];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.Bea;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
|
||||
{
|
||||
internal class X86MOV : X86Instruction
|
||||
{
|
||||
public X86MOV(Disasm rawInstruction) : base()
|
||||
{
|
||||
Operands = new IX86Operand[2];
|
||||
Operands[0] = GetOperand(rawInstruction.Argument1);
|
||||
Operands[1] = GetOperand(rawInstruction.Argument2);
|
||||
}
|
||||
|
||||
public override X86OpCode OpCode
|
||||
{
|
||||
get { return X86OpCode.MOV; }
|
||||
}
|
||||
|
||||
public override void Execute(Dictionary<string, int> registers, Stack<int> localStack)
|
||||
{
|
||||
if (Operands[1] is X86ImmediateOperand)
|
||||
registers[((X86RegisterOperand) Operands[0]).Register.ToString()] =
|
||||
(Operands[1] as X86ImmediateOperand).Immediate;
|
||||
else
|
||||
{
|
||||
var regOperand = (X86RegisterOperand) Operands[0];
|
||||
registers[regOperand.Register.ToString()] =
|
||||
registers[(Operands[1] as X86RegisterOperand).Register.ToString()];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.Bea;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
|
||||
{
|
||||
class X86NEG : X86Instruction
|
||||
{
|
||||
public X86NEG(Disasm rawInstruction) : base()
|
||||
{
|
||||
Operands = new IX86Operand[1];
|
||||
Operands[0] = GetOperand(rawInstruction.Argument1);
|
||||
}
|
||||
|
||||
public override X86OpCode OpCode { get { return X86OpCode.NEG; } }
|
||||
|
||||
public override void Execute(Dictionary<string, int> registers, Stack<int> localStack)
|
||||
{
|
||||
registers[((X86RegisterOperand) Operands[0]).Register.ToString()] =
|
||||
-registers[((X86RegisterOperand) Operands[0]).Register.ToString()];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.Bea;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
|
||||
{
|
||||
class X86NOT : X86Instruction
|
||||
{
|
||||
public X86NOT(Disasm rawInstruction) : base()
|
||||
{
|
||||
Operands = new IX86Operand[1];
|
||||
Operands[0] = GetOperand(rawInstruction.Argument1);
|
||||
}
|
||||
|
||||
public override X86OpCode OpCode { get { return X86OpCode.NOT; } }
|
||||
|
||||
public override void Execute(Dictionary<string, int> registers, Stack<int> localStack)
|
||||
{
|
||||
registers[((X86RegisterOperand)Operands[0]).Register.ToString()] =
|
||||
~registers[((X86RegisterOperand) Operands[0]).Register.ToString()];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.Bea;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
|
||||
{
|
||||
class X86POP : X86Instruction
|
||||
{
|
||||
public X86POP(Disasm rawInstruction) : base()
|
||||
{
|
||||
Operands = new IX86Operand[1];
|
||||
Operands[0] = GetOperand(rawInstruction.Argument1);
|
||||
}
|
||||
|
||||
public override X86OpCode OpCode { get { return X86OpCode.POP; } }
|
||||
|
||||
public override void Execute(Dictionary<string, int> registers, Stack<int> localStack)
|
||||
{
|
||||
// Pretend to pop stack
|
||||
if (localStack.Count < 1)
|
||||
return;
|
||||
|
||||
registers[((X86RegisterOperand) Operands[0]).Register.ToString()] = localStack.Pop();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.Bea;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
|
||||
{
|
||||
class X86PUSH : X86Instruction
|
||||
{
|
||||
public X86PUSH(Disasm rawInstruction) : base()
|
||||
{
|
||||
Operands = new IX86Operand[1];
|
||||
Operands[0] = GetOperand(rawInstruction.Argument1);
|
||||
}
|
||||
|
||||
public override X86OpCode OpCode { get { return X86OpCode.PUSH; } }
|
||||
|
||||
public override void Execute(Dictionary<string, int> registers, Stack<int> localStack)
|
||||
{
|
||||
// Pretend to pop stack
|
||||
if (localStack.Count < 1)
|
||||
return;
|
||||
|
||||
registers[((X86RegisterOperand) Operands[0]).Register.ToString()] = localStack.Pop();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.Bea;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
|
||||
{
|
||||
class X86SUB : X86Instruction
|
||||
{
|
||||
public X86SUB(Disasm rawInstruction) : base()
|
||||
{
|
||||
Operands = new IX86Operand[2];
|
||||
Operands[0] = GetOperand(rawInstruction.Argument1);
|
||||
Operands[1] = GetOperand(rawInstruction.Argument2);
|
||||
}
|
||||
|
||||
public override X86OpCode OpCode { get { return X86OpCode.SUB; } }
|
||||
|
||||
public override void Execute(Dictionary<string, int> registers, Stack<int> localStack)
|
||||
{
|
||||
if (Operands[1] is X86ImmediateOperand)
|
||||
registers[((X86RegisterOperand)Operands[0]).Register.ToString()] -=
|
||||
((X86ImmediateOperand)Operands[1]).Immediate;
|
||||
else
|
||||
registers[((X86RegisterOperand)Operands[0]).Register.ToString()] -=
|
||||
registers[((X86RegisterOperand)Operands[1]).Register.ToString()];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
using de4dot.Bea;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions
|
||||
{
|
||||
class X86XOR : X86Instruction
|
||||
{
|
||||
public X86XOR(Disasm rawInstruction) : base()
|
||||
{
|
||||
Operands = new IX86Operand[2];
|
||||
Operands[0] = GetOperand(rawInstruction.Argument1);
|
||||
Operands[1] = GetOperand(rawInstruction.Argument2);
|
||||
}
|
||||
|
||||
public override X86OpCode OpCode { get { return X86OpCode.XOR; } }
|
||||
|
||||
public override void Execute(Dictionary<string, int> registers, Stack<int> localStack)
|
||||
{
|
||||
if (Operands[1] is X86ImmediateOperand)
|
||||
registers[((X86RegisterOperand)Operands[0]).Register.ToString()] ^=
|
||||
((X86ImmediateOperand)Operands[1]).Immediate;
|
||||
else
|
||||
registers[((X86RegisterOperand)Operands[0]).Register.ToString()] ^=
|
||||
registers[((X86RegisterOperand)Operands[1]).Register.ToString()];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace de4dot.code.x86
|
||||
{
|
||||
public class UnmanagedBuffer
|
||||
{
|
||||
public readonly IntPtr Ptr = IntPtr.Zero;
|
||||
public readonly int Length = 0;
|
||||
|
||||
public UnmanagedBuffer(byte[] data)
|
||||
{
|
||||
Ptr = Marshal.AllocHGlobal(data.Length);
|
||||
Marshal.Copy(data, 0, Ptr, data.Length);
|
||||
Length = data.Length;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
using de4dot.Bea;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86
|
||||
{
|
||||
public enum X86OpCode
|
||||
{
|
||||
MOV,
|
||||
ADD,
|
||||
SUB,
|
||||
IMUL,
|
||||
DIV,
|
||||
NEG,
|
||||
NOT,
|
||||
XOR,
|
||||
POP,
|
||||
PUSH
|
||||
}
|
||||
|
||||
public enum X86Register
|
||||
{
|
||||
EAX = 537001985,
|
||||
ECX = 537001986,
|
||||
EDX = 537001988,
|
||||
EBX = 537001992,
|
||||
ESP = 537001989,
|
||||
EBP = 537001990,
|
||||
ESI = 537002048,
|
||||
EDI = 537002112
|
||||
}
|
||||
|
||||
public interface IX86Operand
|
||||
{
|
||||
}
|
||||
|
||||
public class X86RegisterOperand : IX86Operand
|
||||
{
|
||||
public X86Register Register { get; set; }
|
||||
|
||||
public X86RegisterOperand(X86Register reg)
|
||||
{
|
||||
Register = reg;
|
||||
}
|
||||
}
|
||||
|
||||
public class X86ImmediateOperand : IX86Operand
|
||||
{
|
||||
public int Immediate { get; set; }
|
||||
|
||||
public X86ImmediateOperand(int imm)
|
||||
{
|
||||
Immediate = imm;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class X86Instruction
|
||||
{
|
||||
public abstract X86OpCode OpCode { get; }
|
||||
public IX86Operand[] Operands { get; set; }
|
||||
public abstract void Execute(Dictionary<string, int> registers, Stack<int> localStack);
|
||||
|
||||
public static IX86Operand GetOperand(ArgumentType argument)
|
||||
{
|
||||
if (argument.ArgType == -2013265920)
|
||||
return
|
||||
new X86ImmediateOperand(int.Parse(argument.ArgMnemonic.TrimEnd('h'),
|
||||
NumberStyles.HexNumber));
|
||||
return new X86RegisterOperand((X86Register)argument.ArgType);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using ConfuserDeobfuscator.Engine.Routines.Ex.x86;
|
||||
using ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions;
|
||||
using de4dot.Bea;
|
||||
using de4dot.code.x86;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace de4dot.code.deobfuscators.ConfuserEx.x86
|
||||
{
|
||||
public sealed class X86Method
|
||||
{
|
||||
public List<X86Instruction> Instructions;
|
||||
|
||||
public Stack<int> LocalStack = new Stack<int>();
|
||||
public Dictionary<string, int> Registers = new Dictionary<string, int>
|
||||
{
|
||||
{"EAX", 0},
|
||||
{"EBX", 0},
|
||||
{"ECX", 0},
|
||||
{"EDX", 0},
|
||||
{"ESP", 0},
|
||||
{"EBP", 0},
|
||||
{"ESI", 0},
|
||||
{"EDI", 0}
|
||||
};
|
||||
|
||||
readonly ModuleDefMD _module;
|
||||
public X86Method(MethodDef method,ModuleDefMD module)
|
||||
{
|
||||
this._module = module;
|
||||
Instructions = new List<X86Instruction>();
|
||||
ParseInstructions(method);
|
||||
}
|
||||
|
||||
private void ParseInstructions(MethodDef method)
|
||||
{
|
||||
var rawInstructions = new List<Disasm>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
byte[] bytes = ReadChunk(method, _module);
|
||||
|
||||
var disasm = new Disasm();
|
||||
var buff = new UnmanagedBuffer(bytes);
|
||||
|
||||
disasm.EIP = new IntPtr(buff.Ptr.ToInt32());
|
||||
|
||||
var instruction = BeaEngine.Disasm(disasm);
|
||||
_readOffset -= 8 - instruction; // revert offset back for each byte that was not a part of this instruction
|
||||
var mnemonic = disasm.Instruction.Mnemonic.Trim();
|
||||
|
||||
if (mnemonic == "ret") //TODO: Check if this is the only return in function, e.g. check for jumps that go beyond this address
|
||||
{
|
||||
Marshal.FreeHGlobal(buff.Ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
rawInstructions.Add(Clone(disasm));
|
||||
//disasm.EIP = new IntPtr(disasm.EIP.ToInt32() + instruction);
|
||||
|
||||
Marshal.FreeHGlobal(buff.Ptr);
|
||||
}
|
||||
|
||||
//while(rawInstructions.First().Instruction.Mnemonic.Trim() == "pop")
|
||||
// rawInstructions.Remove(rawInstructions.First());
|
||||
|
||||
while (rawInstructions.Last().Instruction.Mnemonic.Trim() == "pop")
|
||||
rawInstructions.Remove(rawInstructions.Last());
|
||||
|
||||
|
||||
foreach (var instr in rawInstructions)
|
||||
{
|
||||
switch (instr.Instruction.Mnemonic.Trim())
|
||||
{
|
||||
case "mov":
|
||||
Instructions.Add(new X86MOV(instr));
|
||||
break;
|
||||
case "add":
|
||||
Instructions.Add(new X86ADD(instr));
|
||||
break;
|
||||
case "sub":
|
||||
Instructions.Add(new X86SUB(instr));
|
||||
break;
|
||||
case "imul":
|
||||
Instructions.Add(new X86IMUL(instr));
|
||||
break;
|
||||
case "div":
|
||||
Instructions.Add(new X86DIV(instr));
|
||||
break;
|
||||
case "neg":
|
||||
Instructions.Add(new X86NEG(instr));
|
||||
break;
|
||||
case "not":
|
||||
Instructions.Add(new X86NOT(instr));
|
||||
break;
|
||||
case "xor":
|
||||
Instructions.Add(new X86XOR(instr));
|
||||
break;
|
||||
case "pop":
|
||||
Instructions.Add(new X86POP(instr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int _readOffset;
|
||||
public byte[] ReadChunk(MethodDef method, ModuleDefMD module)
|
||||
{
|
||||
var stream = module.MetaData.PEImage.CreateFullStream();
|
||||
var offset = module.MetaData.PEImage.ToFileOffset(method.RVA);
|
||||
|
||||
byte[] buffer = new byte[8];
|
||||
|
||||
if (_readOffset == 0) //TODO: Don't use hardcoded offset
|
||||
_readOffset = (int) offset + 20; // skip to actual calculation code
|
||||
|
||||
stream.Position = _readOffset;
|
||||
|
||||
stream.Read(buffer, 0, 8); // read 8 bytes to make sure that's a whole instruction
|
||||
_readOffset += 8;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public int Execute(params int[] @params)
|
||||
{
|
||||
foreach (var param in @params)
|
||||
LocalStack.Push(param);
|
||||
|
||||
foreach (var instr in Instructions)
|
||||
instr.Execute(Registers, LocalStack);
|
||||
|
||||
return Registers["EAX"];
|
||||
}
|
||||
|
||||
|
||||
public static Disasm Clone(Disasm disasm)
|
||||
{
|
||||
return new Disasm
|
||||
{
|
||||
Archi = disasm.Archi,
|
||||
Argument1 = disasm.Argument1,
|
||||
Argument2 = disasm.Argument2,
|
||||
Argument3 = disasm.Argument3,
|
||||
CompleteInstr = disasm.CompleteInstr,
|
||||
EIP = disasm.EIP,
|
||||
Instruction = disasm.Instruction,
|
||||
Options = disasm.Options,
|
||||
Prefix = disasm.Prefix,
|
||||
SecurityBlock = disasm.SecurityBlock,
|
||||
VirtualAddr = disasm.VirtualAddr
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,688 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace de4dot.code.deobfuscators
|
||||
{
|
||||
internal static class Lzma
|
||||
{
|
||||
const uint kNumStates = 12;
|
||||
|
||||
const int kNumPosSlotBits = 6;
|
||||
|
||||
const uint kNumLenToPosStates = 4;
|
||||
|
||||
const uint kMatchMinLen = 2;
|
||||
|
||||
const int kNumAlignBits = 4;
|
||||
const uint kAlignTableSize = 1 << kNumAlignBits;
|
||||
|
||||
const uint kStartPosModelIndex = 4;
|
||||
const uint kEndPosModelIndex = 14;
|
||||
|
||||
const uint kNumFullDistances = 1 << ((int)kEndPosModelIndex / 2);
|
||||
|
||||
const int kNumPosStatesBitsMax = 4;
|
||||
const uint kNumPosStatesMax = (1 << kNumPosStatesBitsMax);
|
||||
|
||||
const int kNumLowLenBits = 3;
|
||||
const int kNumMidLenBits = 3;
|
||||
const int kNumHighLenBits = 8;
|
||||
const uint kNumLowLenSymbols = 1 << kNumLowLenBits;
|
||||
const uint kNumMidLenSymbols = 1 << kNumMidLenBits;
|
||||
|
||||
public static byte[] Decompress(byte[] data)
|
||||
{
|
||||
var s = new MemoryStream(data);
|
||||
var decoder = new LzmaDecoder();
|
||||
var prop = new byte[5];
|
||||
s.Read(prop, 0, 5);
|
||||
decoder.SetDecoderProperties(prop);
|
||||
long outSize = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
int v = s.ReadByte();
|
||||
outSize |= ((long)(byte)v) << (8 * i);
|
||||
}
|
||||
var b = new byte[(int)outSize];
|
||||
var z = new MemoryStream(b, true);
|
||||
long compressedSize = s.Length - 13;
|
||||
decoder.Code(s, z, compressedSize, outSize);
|
||||
return b;
|
||||
}
|
||||
|
||||
struct BitDecoder
|
||||
{
|
||||
public const int kNumBitModelTotalBits = 11;
|
||||
public const uint kBitModelTotal = (1 << kNumBitModelTotalBits);
|
||||
const int kNumMoveBits = 5;
|
||||
|
||||
uint Prob;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
Prob = kBitModelTotal >> 1;
|
||||
}
|
||||
|
||||
public uint Decode(Decoder rangeDecoder)
|
||||
{
|
||||
uint newBound = (rangeDecoder.Range >> kNumBitModelTotalBits) * Prob;
|
||||
if (rangeDecoder.Code < newBound)
|
||||
{
|
||||
rangeDecoder.Range = newBound;
|
||||
Prob += (kBitModelTotal - Prob) >> kNumMoveBits;
|
||||
if (rangeDecoder.Range < Decoder.kTopValue)
|
||||
{
|
||||
rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
|
||||
rangeDecoder.Range <<= 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
rangeDecoder.Range -= newBound;
|
||||
rangeDecoder.Code -= newBound;
|
||||
Prob -= (Prob) >> kNumMoveBits;
|
||||
if (rangeDecoder.Range < Decoder.kTopValue)
|
||||
{
|
||||
rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
|
||||
rangeDecoder.Range <<= 8;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
struct BitTreeDecoder
|
||||
{
|
||||
readonly BitDecoder[] Models;
|
||||
readonly int NumBitLevels;
|
||||
|
||||
public BitTreeDecoder(int numBitLevels)
|
||||
{
|
||||
NumBitLevels = numBitLevels;
|
||||
Models = new BitDecoder[1 << numBitLevels];
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
for (uint i = 1; i < (1 << NumBitLevels); i++)
|
||||
Models[i].Init();
|
||||
}
|
||||
|
||||
public uint Decode(Decoder rangeDecoder)
|
||||
{
|
||||
uint m = 1;
|
||||
for (int bitIndex = NumBitLevels; bitIndex > 0; bitIndex--)
|
||||
m = (m << 1) + Models[m].Decode(rangeDecoder);
|
||||
return m - ((uint)1 << NumBitLevels);
|
||||
}
|
||||
|
||||
public uint ReverseDecode(Decoder rangeDecoder)
|
||||
{
|
||||
uint m = 1;
|
||||
uint symbol = 0;
|
||||
for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
|
||||
{
|
||||
uint bit = Models[m].Decode(rangeDecoder);
|
||||
m <<= 1;
|
||||
m += bit;
|
||||
symbol |= (bit << bitIndex);
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
public static uint ReverseDecode(BitDecoder[] Models, UInt32 startIndex,
|
||||
Decoder rangeDecoder, int NumBitLevels)
|
||||
{
|
||||
uint m = 1;
|
||||
uint symbol = 0;
|
||||
for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
|
||||
{
|
||||
uint bit = Models[startIndex + m].Decode(rangeDecoder);
|
||||
m <<= 1;
|
||||
m += bit;
|
||||
symbol |= (bit << bitIndex);
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
|
||||
class Decoder
|
||||
{
|
||||
public const uint kTopValue = (1 << 24);
|
||||
public uint Code;
|
||||
public uint Range;
|
||||
public Stream Stream;
|
||||
|
||||
public void Init(Stream stream)
|
||||
{
|
||||
// Stream.Init(stream);
|
||||
Stream = stream;
|
||||
|
||||
Code = 0;
|
||||
Range = 0xFFFFFFFF;
|
||||
for (int i = 0; i < 5; i++)
|
||||
Code = (Code << 8) | (byte)Stream.ReadByte();
|
||||
}
|
||||
|
||||
public void ReleaseStream()
|
||||
{
|
||||
Stream = null;
|
||||
}
|
||||
|
||||
public void Normalize()
|
||||
{
|
||||
while (Range < kTopValue)
|
||||
{
|
||||
Code = (Code << 8) | (byte)Stream.ReadByte();
|
||||
Range <<= 8;
|
||||
}
|
||||
}
|
||||
|
||||
public uint DecodeDirectBits(int numTotalBits)
|
||||
{
|
||||
uint range = Range;
|
||||
uint code = Code;
|
||||
uint result = 0;
|
||||
for (int i = numTotalBits; i > 0; i--)
|
||||
{
|
||||
range >>= 1;
|
||||
/*
|
||||
result <<= 1;
|
||||
if (code >= range)
|
||||
{
|
||||
code -= range;
|
||||
result |= 1;
|
||||
}
|
||||
*/
|
||||
uint t = (code - range) >> 31;
|
||||
code -= range & (t - 1);
|
||||
result = (result << 1) | (1 - t);
|
||||
|
||||
if (range < kTopValue)
|
||||
{
|
||||
code = (code << 8) | (byte)Stream.ReadByte();
|
||||
range <<= 8;
|
||||
}
|
||||
}
|
||||
Range = range;
|
||||
Code = code;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class LzmaDecoder
|
||||
{
|
||||
readonly BitDecoder[] m_IsMatchDecoders = new BitDecoder[kNumStates << kNumPosStatesBitsMax];
|
||||
readonly BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[kNumStates << kNumPosStatesBitsMax];
|
||||
readonly BitDecoder[] m_IsRepDecoders = new BitDecoder[kNumStates];
|
||||
readonly BitDecoder[] m_IsRepG0Decoders = new BitDecoder[kNumStates];
|
||||
readonly BitDecoder[] m_IsRepG1Decoders = new BitDecoder[kNumStates];
|
||||
readonly BitDecoder[] m_IsRepG2Decoders = new BitDecoder[kNumStates];
|
||||
|
||||
readonly LenDecoder m_LenDecoder = new LenDecoder();
|
||||
|
||||
readonly LiteralDecoder m_LiteralDecoder = new LiteralDecoder();
|
||||
readonly OutWindow m_OutWindow = new OutWindow();
|
||||
readonly BitDecoder[] m_PosDecoders = new BitDecoder[kNumFullDistances - kEndPosModelIndex];
|
||||
readonly BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[kNumLenToPosStates];
|
||||
readonly Decoder m_RangeDecoder = new Decoder();
|
||||
readonly LenDecoder m_RepLenDecoder = new LenDecoder();
|
||||
bool _solid = false;
|
||||
|
||||
uint m_DictionarySize;
|
||||
uint m_DictionarySizeCheck;
|
||||
BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(kNumAlignBits);
|
||||
|
||||
uint m_PosStateMask;
|
||||
|
||||
public LzmaDecoder()
|
||||
{
|
||||
m_DictionarySize = 0xFFFFFFFF;
|
||||
for (int i = 0; i < kNumLenToPosStates; i++)
|
||||
m_PosSlotDecoder[i] = new BitTreeDecoder(kNumPosSlotBits);
|
||||
}
|
||||
|
||||
void SetDictionarySize(uint dictionarySize)
|
||||
{
|
||||
if (m_DictionarySize != dictionarySize)
|
||||
{
|
||||
m_DictionarySize = dictionarySize;
|
||||
m_DictionarySizeCheck = Math.Max(m_DictionarySize, 1);
|
||||
uint blockSize = Math.Max(m_DictionarySizeCheck, (1 << 12));
|
||||
m_OutWindow.Create(blockSize);
|
||||
}
|
||||
}
|
||||
|
||||
void SetLiteralProperties(int lp, int lc)
|
||||
{
|
||||
m_LiteralDecoder.Create(lp, lc);
|
||||
}
|
||||
|
||||
void SetPosBitsProperties(int pb)
|
||||
{
|
||||
uint numPosStates = (uint)1 << pb;
|
||||
m_LenDecoder.Create(numPosStates);
|
||||
m_RepLenDecoder.Create(numPosStates);
|
||||
m_PosStateMask = numPosStates - 1;
|
||||
}
|
||||
|
||||
void Init(Stream inStream, Stream outStream)
|
||||
{
|
||||
m_RangeDecoder.Init(inStream);
|
||||
m_OutWindow.Init(outStream, _solid);
|
||||
|
||||
uint i;
|
||||
for (i = 0; i < kNumStates; i++)
|
||||
{
|
||||
for (uint j = 0; j <= m_PosStateMask; j++)
|
||||
{
|
||||
uint index = (i << kNumPosStatesBitsMax) + j;
|
||||
m_IsMatchDecoders[index].Init();
|
||||
m_IsRep0LongDecoders[index].Init();
|
||||
}
|
||||
m_IsRepDecoders[i].Init();
|
||||
m_IsRepG0Decoders[i].Init();
|
||||
m_IsRepG1Decoders[i].Init();
|
||||
m_IsRepG2Decoders[i].Init();
|
||||
}
|
||||
|
||||
m_LiteralDecoder.Init();
|
||||
for (i = 0; i < kNumLenToPosStates; i++)
|
||||
m_PosSlotDecoder[i].Init();
|
||||
// m_PosSpecDecoder.Init();
|
||||
for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++)
|
||||
m_PosDecoders[i].Init();
|
||||
|
||||
m_LenDecoder.Init();
|
||||
m_RepLenDecoder.Init();
|
||||
m_PosAlignDecoder.Init();
|
||||
}
|
||||
|
||||
public void Code(Stream inStream, Stream outStream,
|
||||
Int64 inSize, Int64 outSize)
|
||||
{
|
||||
Init(inStream, outStream);
|
||||
|
||||
var state = new State();
|
||||
state.Init();
|
||||
uint rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0;
|
||||
|
||||
UInt64 nowPos64 = 0;
|
||||
var outSize64 = (UInt64)outSize;
|
||||
if (nowPos64 < outSize64)
|
||||
{
|
||||
m_IsMatchDecoders[state.Index << kNumPosStatesBitsMax].Decode(m_RangeDecoder);
|
||||
state.UpdateChar();
|
||||
byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0, 0);
|
||||
m_OutWindow.PutByte(b);
|
||||
nowPos64++;
|
||||
}
|
||||
while (nowPos64 < outSize64)
|
||||
{
|
||||
// UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64);
|
||||
// while(nowPos64 < next)
|
||||
{
|
||||
uint posState = (uint)nowPos64 & m_PosStateMask;
|
||||
if (m_IsMatchDecoders[(state.Index << kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0)
|
||||
{
|
||||
byte b;
|
||||
byte prevByte = m_OutWindow.GetByte(0);
|
||||
if (!state.IsCharState())
|
||||
b = m_LiteralDecoder.DecodeWithMatchByte(m_RangeDecoder,
|
||||
(uint)nowPos64, prevByte, m_OutWindow.GetByte(rep0));
|
||||
else
|
||||
b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, (uint)nowPos64, prevByte);
|
||||
m_OutWindow.PutByte(b);
|
||||
state.UpdateChar();
|
||||
nowPos64++;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint len;
|
||||
if (m_IsRepDecoders[state.Index].Decode(m_RangeDecoder) == 1)
|
||||
{
|
||||
if (m_IsRepG0Decoders[state.Index].Decode(m_RangeDecoder) == 0)
|
||||
{
|
||||
if (m_IsRep0LongDecoders[(state.Index << kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0)
|
||||
{
|
||||
state.UpdateShortRep();
|
||||
m_OutWindow.PutByte(m_OutWindow.GetByte(rep0));
|
||||
nowPos64++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 distance;
|
||||
if (m_IsRepG1Decoders[state.Index].Decode(m_RangeDecoder) == 0)
|
||||
{
|
||||
distance = rep1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_IsRepG2Decoders[state.Index].Decode(m_RangeDecoder) == 0)
|
||||
distance = rep2;
|
||||
else
|
||||
{
|
||||
distance = rep3;
|
||||
rep3 = rep2;
|
||||
}
|
||||
rep2 = rep1;
|
||||
}
|
||||
rep1 = rep0;
|
||||
rep0 = distance;
|
||||
}
|
||||
len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + kMatchMinLen;
|
||||
state.UpdateRep();
|
||||
}
|
||||
else
|
||||
{
|
||||
rep3 = rep2;
|
||||
rep2 = rep1;
|
||||
rep1 = rep0;
|
||||
len = kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState);
|
||||
state.UpdateMatch();
|
||||
uint posSlot = m_PosSlotDecoder[GetLenToPosState(len)].Decode(m_RangeDecoder);
|
||||
if (posSlot >= kStartPosModelIndex)
|
||||
{
|
||||
var numDirectBits = (int)((posSlot >> 1) - 1);
|
||||
rep0 = ((2 | (posSlot & 1)) << numDirectBits);
|
||||
if (posSlot < kEndPosModelIndex)
|
||||
rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders,
|
||||
rep0 - posSlot - 1, m_RangeDecoder, numDirectBits);
|
||||
else
|
||||
{
|
||||
rep0 += (m_RangeDecoder.DecodeDirectBits(
|
||||
numDirectBits - kNumAlignBits) << kNumAlignBits);
|
||||
rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder);
|
||||
}
|
||||
}
|
||||
else
|
||||
rep0 = posSlot;
|
||||
}
|
||||
if (rep0 >= nowPos64 || rep0 >= m_DictionarySizeCheck)
|
||||
{
|
||||
if (rep0 == 0xFFFFFFFF)
|
||||
break;
|
||||
}
|
||||
m_OutWindow.CopyBlock(rep0, len);
|
||||
nowPos64 += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_OutWindow.Flush();
|
||||
m_OutWindow.ReleaseStream();
|
||||
m_RangeDecoder.ReleaseStream();
|
||||
}
|
||||
|
||||
public void SetDecoderProperties(byte[] properties)
|
||||
{
|
||||
int lc = properties[0] % 9;
|
||||
int remainder = properties[0] / 9;
|
||||
int lp = remainder % 5;
|
||||
int pb = remainder / 5;
|
||||
UInt32 dictionarySize = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8);
|
||||
SetDictionarySize(dictionarySize);
|
||||
SetLiteralProperties(lp, lc);
|
||||
SetPosBitsProperties(pb);
|
||||
}
|
||||
|
||||
static uint GetLenToPosState(uint len)
|
||||
{
|
||||
len -= kMatchMinLen;
|
||||
if (len < kNumLenToPosStates)
|
||||
return len;
|
||||
return unchecked((kNumLenToPosStates - 1));
|
||||
}
|
||||
|
||||
class LenDecoder
|
||||
{
|
||||
readonly BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[kNumPosStatesMax];
|
||||
readonly BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[kNumPosStatesMax];
|
||||
BitDecoder m_Choice = new BitDecoder();
|
||||
BitDecoder m_Choice2 = new BitDecoder();
|
||||
BitTreeDecoder m_HighCoder = new BitTreeDecoder(kNumHighLenBits);
|
||||
uint m_NumPosStates;
|
||||
|
||||
public void Create(uint numPosStates)
|
||||
{
|
||||
for (uint posState = m_NumPosStates; posState < numPosStates; posState++)
|
||||
{
|
||||
m_LowCoder[posState] = new BitTreeDecoder(kNumLowLenBits);
|
||||
m_MidCoder[posState] = new BitTreeDecoder(kNumMidLenBits);
|
||||
}
|
||||
m_NumPosStates = numPosStates;
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
m_Choice.Init();
|
||||
for (uint posState = 0; posState < m_NumPosStates; posState++)
|
||||
{
|
||||
m_LowCoder[posState].Init();
|
||||
m_MidCoder[posState].Init();
|
||||
}
|
||||
m_Choice2.Init();
|
||||
m_HighCoder.Init();
|
||||
}
|
||||
|
||||
public uint Decode(Decoder rangeDecoder, uint posState)
|
||||
{
|
||||
if (m_Choice.Decode(rangeDecoder) == 0)
|
||||
return m_LowCoder[posState].Decode(rangeDecoder);
|
||||
uint symbol = kNumLowLenSymbols;
|
||||
if (m_Choice2.Decode(rangeDecoder) == 0)
|
||||
symbol += m_MidCoder[posState].Decode(rangeDecoder);
|
||||
else
|
||||
{
|
||||
symbol += kNumMidLenSymbols;
|
||||
symbol += m_HighCoder.Decode(rangeDecoder);
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
|
||||
class LiteralDecoder
|
||||
{
|
||||
Decoder2[] m_Coders;
|
||||
int m_NumPosBits;
|
||||
int m_NumPrevBits;
|
||||
uint m_PosMask;
|
||||
|
||||
public void Create(int numPosBits, int numPrevBits)
|
||||
{
|
||||
if (m_Coders != null && m_NumPrevBits == numPrevBits &&
|
||||
m_NumPosBits == numPosBits)
|
||||
return;
|
||||
m_NumPosBits = numPosBits;
|
||||
m_PosMask = ((uint)1 << numPosBits) - 1;
|
||||
m_NumPrevBits = numPrevBits;
|
||||
uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
|
||||
m_Coders = new Decoder2[numStates];
|
||||
for (uint i = 0; i < numStates; i++)
|
||||
m_Coders[i].Create();
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
|
||||
for (uint i = 0; i < numStates; i++)
|
||||
m_Coders[i].Init();
|
||||
}
|
||||
|
||||
uint GetState(uint pos, byte prevByte)
|
||||
{
|
||||
return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits));
|
||||
}
|
||||
|
||||
public byte DecodeNormal(Decoder rangeDecoder, uint pos, byte prevByte)
|
||||
{
|
||||
return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder);
|
||||
}
|
||||
|
||||
public byte DecodeWithMatchByte(Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte)
|
||||
{
|
||||
return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte);
|
||||
}
|
||||
|
||||
struct Decoder2
|
||||
{
|
||||
BitDecoder[] m_Decoders;
|
||||
|
||||
public void Create()
|
||||
{
|
||||
m_Decoders = new BitDecoder[0x300];
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
for (int i = 0; i < 0x300; i++) m_Decoders[i].Init();
|
||||
}
|
||||
|
||||
public byte DecodeNormal(Decoder rangeDecoder)
|
||||
{
|
||||
uint symbol = 1;
|
||||
do
|
||||
symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); while (symbol < 0x100);
|
||||
return (byte)symbol;
|
||||
}
|
||||
|
||||
public byte DecodeWithMatchByte(Decoder rangeDecoder, byte matchByte)
|
||||
{
|
||||
uint symbol = 1;
|
||||
do
|
||||
{
|
||||
uint matchBit = (uint)(matchByte >> 7) & 1;
|
||||
matchByte <<= 1;
|
||||
uint bit = m_Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder);
|
||||
symbol = (symbol << 1) | bit;
|
||||
if (matchBit != bit)
|
||||
{
|
||||
while (symbol < 0x100)
|
||||
symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder);
|
||||
break;
|
||||
}
|
||||
} while (symbol < 0x100);
|
||||
return (byte)symbol;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class OutWindow
|
||||
{
|
||||
byte[] _buffer;
|
||||
uint _pos;
|
||||
Stream _stream;
|
||||
uint _streamPos;
|
||||
uint _windowSize;
|
||||
|
||||
public void Create(uint windowSize)
|
||||
{
|
||||
if (_windowSize != windowSize)
|
||||
{
|
||||
_buffer = new byte[windowSize];
|
||||
}
|
||||
_windowSize = windowSize;
|
||||
_pos = 0;
|
||||
_streamPos = 0;
|
||||
}
|
||||
|
||||
public void Init(Stream stream, bool solid)
|
||||
{
|
||||
ReleaseStream();
|
||||
_stream = stream;
|
||||
if (!solid)
|
||||
{
|
||||
_streamPos = 0;
|
||||
_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseStream()
|
||||
{
|
||||
Flush();
|
||||
_stream = null;
|
||||
Buffer.BlockCopy(new byte[_buffer.Length], 0, _buffer, 0, _buffer.Length);
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
uint size = _pos - _streamPos;
|
||||
if (size == 0)
|
||||
return;
|
||||
_stream.Write(_buffer, (int)_streamPos, (int)size);
|
||||
if (_pos >= _windowSize)
|
||||
_pos = 0;
|
||||
_streamPos = _pos;
|
||||
}
|
||||
|
||||
public void CopyBlock(uint distance, uint len)
|
||||
{
|
||||
uint pos = _pos - distance - 1;
|
||||
if (pos >= _windowSize)
|
||||
pos += _windowSize;
|
||||
for (; len > 0; len--)
|
||||
{
|
||||
if (pos >= _windowSize)
|
||||
pos = 0;
|
||||
_buffer[_pos++] = _buffer[pos++];
|
||||
if (_pos >= _windowSize)
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void PutByte(byte b)
|
||||
{
|
||||
_buffer[_pos++] = b;
|
||||
if (_pos >= _windowSize)
|
||||
Flush();
|
||||
}
|
||||
|
||||
public byte GetByte(uint distance)
|
||||
{
|
||||
uint pos = _pos - distance - 1;
|
||||
if (pos >= _windowSize)
|
||||
pos += _windowSize;
|
||||
return _buffer[pos];
|
||||
}
|
||||
}
|
||||
|
||||
struct State
|
||||
{
|
||||
public uint Index;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
Index = 0;
|
||||
}
|
||||
|
||||
public void UpdateChar()
|
||||
{
|
||||
if (Index < 4) Index = 0;
|
||||
else if (Index < 10) Index -= 3;
|
||||
else Index -= 6;
|
||||
}
|
||||
|
||||
public void UpdateMatch()
|
||||
{
|
||||
Index = (uint)(Index < 7 ? 7 : 10);
|
||||
}
|
||||
|
||||
public void UpdateRep()
|
||||
{
|
||||
Index = (uint)(Index < 7 ? 8 : 11);
|
||||
}
|
||||
|
||||
public void UpdateShortRep()
|
||||
{
|
||||
Index = (uint)(Index < 7 ? 9 : 11);
|
||||
}
|
||||
|
||||
public bool IsCharState()
|
||||
{
|
||||
return Index < 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,214 +1,215 @@
|
|||
/*
|
||||
Copyright (C) 2011-2015 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.Text;
|
||||
using dnlib.DotNet;
|
||||
using de4dot.code;
|
||||
using de4dot.code.deobfuscators;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace de4dot.cui {
|
||||
class ExitException : Exception {
|
||||
public readonly int code;
|
||||
public ExitException(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
class Program {
|
||||
static IList<IDeobfuscatorInfo> deobfuscatorInfos = CreateDeobfuscatorInfos();
|
||||
|
||||
static IList<IDeobfuscatorInfo> LoadPlugin(string assembly) {
|
||||
var plugins = new List<IDeobfuscatorInfo>();
|
||||
try {
|
||||
foreach (Type item in Assembly.LoadFile(assembly).GetTypes()) {
|
||||
var interfaces = new List<Type>(item.GetInterfaces());
|
||||
if (item.IsClass && interfaces.Contains(typeof(IDeobfuscatorInfo)))
|
||||
plugins.Add((IDeobfuscatorInfo)Activator.CreateInstance(item));
|
||||
}
|
||||
}
|
||||
catch {
|
||||
}
|
||||
return plugins;
|
||||
}
|
||||
|
||||
public static void GetPlugins(string directory, ref Dictionary<string, IDeobfuscatorInfo> result) {
|
||||
var plugins = new List<IDeobfuscatorInfo>();
|
||||
try {
|
||||
var files = Directory.GetFiles(directory, "deobfuscator.*.dll", SearchOption.TopDirectoryOnly);
|
||||
foreach (var file in files)
|
||||
plugins.AddRange(LoadPlugin(Path.GetFullPath(file)));
|
||||
}
|
||||
catch {
|
||||
}
|
||||
foreach(var p in plugins)
|
||||
result[p.Type] = p;
|
||||
}
|
||||
|
||||
static IList<IDeobfuscatorInfo> CreateDeobfuscatorInfos() {
|
||||
var local = new List<IDeobfuscatorInfo> {
|
||||
new de4dot.code.deobfuscators.Unknown.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Agile_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Babel_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.CodeFort.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.CodeVeil.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.CodeWall.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Confuser.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.CryptoObfuscator.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.DeepSea.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Dotfuscator.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.dotNET_Reactor.v3.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.dotNET_Reactor.v4.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Eazfuscator_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Goliath_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.ILProtector.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.MaxtoCode.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.MPRESS.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Rummage.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Skater_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.SmartAssembly.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Spices_Net.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Xenocode.DeobfuscatorInfo(),
|
||||
};
|
||||
var dict = new Dictionary<string, IDeobfuscatorInfo>();
|
||||
foreach (var d in local)
|
||||
dict[d.Type] = d;
|
||||
string pluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin");
|
||||
GetPlugins(pluginDir, ref dict);
|
||||
return new List<IDeobfuscatorInfo>(dict.Values);
|
||||
}
|
||||
|
||||
public static int Main(string[] args) {
|
||||
int exitCode = 0;
|
||||
|
||||
const string showAllMessagesEnvName = "SHOWALLMESSAGES";
|
||||
try {
|
||||
if (Console.OutputEncoding.IsSingleByte)
|
||||
Console.OutputEncoding = new UTF8Encoding(false);
|
||||
|
||||
Logger.Instance.CanIgnoreMessages = !HasEnv(showAllMessagesEnvName);
|
||||
|
||||
Logger.n("");
|
||||
Logger.n("de4dot v{0} Copyright (C) 2011-2015 de4dot@gmail.com", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
|
||||
Logger.n("Latest version and source code: https://github.com/0xd4d/de4dot");
|
||||
Logger.n("");
|
||||
|
||||
var options = new FilesDeobfuscator.Options();
|
||||
ParseCommandLine(args, options);
|
||||
new FilesDeobfuscator(options).DoIt();
|
||||
}
|
||||
catch (ExitException ex) {
|
||||
exitCode = ex.code;
|
||||
}
|
||||
catch (UserException ex) {
|
||||
Logger.Instance.LogErrorDontIgnore("{0}", ex.Message);
|
||||
exitCode = 1;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (PrintFullStackTrace()) {
|
||||
PrintStackTrace(ex);
|
||||
Logger.Instance.LogErrorDontIgnore("\nTry the latest version!");
|
||||
}
|
||||
else {
|
||||
Logger.Instance.LogErrorDontIgnore("\n\n");
|
||||
Logger.Instance.LogErrorDontIgnore("Hmmmm... something didn't work. Try the latest version.");
|
||||
}
|
||||
exitCode = 1;
|
||||
}
|
||||
|
||||
if (Logger.Instance.NumIgnoredMessages > 0) {
|
||||
if (Logger.Instance.NumIgnoredMessages == 1)
|
||||
Logger.n("Ignored {0} warning/error", Logger.Instance.NumIgnoredMessages);
|
||||
else
|
||||
Logger.n("Ignored {0} warnings/errors", Logger.Instance.NumIgnoredMessages);
|
||||
Logger.n("Use -v/-vv option or set environment variable {0}=1 to see all messages", showAllMessagesEnvName);
|
||||
}
|
||||
|
||||
if (IsN00bUser()) {
|
||||
Console.Error.WriteLine("\n\nPress any key to exit...\n");
|
||||
try {
|
||||
Console.ReadKey(true);
|
||||
}
|
||||
catch (InvalidOperationException) {
|
||||
}
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
static bool PrintFullStackTrace() {
|
||||
if (!Logger.Instance.IgnoresEvent(LoggerEvent.Verbose))
|
||||
return true;
|
||||
if (HasEnv("STACKTRACE"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool HasEnv(string name) {
|
||||
foreach (var tmp in Environment.GetEnvironmentVariables().Keys) {
|
||||
var env = tmp as string;
|
||||
if (env == null)
|
||||
continue;
|
||||
if (string.Equals(env, name, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsN00bUser() {
|
||||
if (HasEnv("VisualStudioDir"))
|
||||
return false;
|
||||
if (HasEnv("SHELL"))
|
||||
return false;
|
||||
return HasEnv("windir") && !HasEnv("PROMPT");
|
||||
}
|
||||
|
||||
public static void PrintStackTrace(Exception ex) {
|
||||
PrintStackTrace(ex, LoggerEvent.Error);
|
||||
}
|
||||
|
||||
public static void PrintStackTrace(Exception ex, LoggerEvent loggerEvent) {
|
||||
var line = new string('-', 78);
|
||||
Logger.Instance.Log(false, null, loggerEvent, "\n\n");
|
||||
Logger.Instance.Log(false, null, loggerEvent, line);
|
||||
Logger.Instance.Log(false, null, loggerEvent, "Stack trace:\n{0}", ex.StackTrace);
|
||||
Logger.Instance.Log(false, null, loggerEvent, "\n\nCaught an exception:\n");
|
||||
Logger.Instance.Log(false, null, loggerEvent, line);
|
||||
Logger.Instance.Log(false, null, loggerEvent, "Message:");
|
||||
Logger.Instance.Log(false, null, loggerEvent, " {0}", ex.Message);
|
||||
Logger.Instance.Log(false, null, loggerEvent, "Type:");
|
||||
Logger.Instance.Log(false, null, loggerEvent, " {0}", ex.GetType());
|
||||
Logger.Instance.Log(false, null, loggerEvent, line);
|
||||
}
|
||||
|
||||
static void ParseCommandLine(string[] args, FilesDeobfuscator.Options options) {
|
||||
new CommandLineParser(deobfuscatorInfos, options).Parse(args);
|
||||
|
||||
Logger.vv("Args:");
|
||||
Logger.Instance.Indent();
|
||||
foreach (var arg in args)
|
||||
Logger.vv("{0}", Utils.ToCsharpString(arg));
|
||||
Logger.Instance.DeIndent();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Copyright (C) 2011-2015 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.Text;
|
||||
using dnlib.DotNet;
|
||||
using de4dot.code;
|
||||
using de4dot.code.deobfuscators;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace de4dot.cui {
|
||||
class ExitException : Exception {
|
||||
public readonly int code;
|
||||
public ExitException(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
class Program {
|
||||
static IList<IDeobfuscatorInfo> deobfuscatorInfos = CreateDeobfuscatorInfos();
|
||||
|
||||
static IList<IDeobfuscatorInfo> LoadPlugin(string assembly) {
|
||||
var plugins = new List<IDeobfuscatorInfo>();
|
||||
try {
|
||||
foreach (Type item in Assembly.LoadFile(assembly).GetTypes()) {
|
||||
var interfaces = new List<Type>(item.GetInterfaces());
|
||||
if (item.IsClass && interfaces.Contains(typeof(IDeobfuscatorInfo)))
|
||||
plugins.Add((IDeobfuscatorInfo)Activator.CreateInstance(item));
|
||||
}
|
||||
}
|
||||
catch {
|
||||
}
|
||||
return plugins;
|
||||
}
|
||||
|
||||
public static void GetPlugins(string directory, ref Dictionary<string, IDeobfuscatorInfo> result) {
|
||||
var plugins = new List<IDeobfuscatorInfo>();
|
||||
try {
|
||||
var files = Directory.GetFiles(directory, "deobfuscator.*.dll", SearchOption.TopDirectoryOnly);
|
||||
foreach (var file in files)
|
||||
plugins.AddRange(LoadPlugin(Path.GetFullPath(file)));
|
||||
}
|
||||
catch {
|
||||
}
|
||||
foreach(var p in plugins)
|
||||
result[p.Type] = p;
|
||||
}
|
||||
|
||||
static IList<IDeobfuscatorInfo> CreateDeobfuscatorInfos() {
|
||||
var local = new List<IDeobfuscatorInfo> {
|
||||
new de4dot.code.deobfuscators.Unknown.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Agile_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Babel_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.CodeFort.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.CodeVeil.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.CodeWall.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Confuser.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.ConfuserEx.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.CryptoObfuscator.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.DeepSea.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Dotfuscator.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.dotNET_Reactor.v3.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.dotNET_Reactor.v4.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Eazfuscator_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Goliath_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.ILProtector.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.MaxtoCode.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.MPRESS.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Rummage.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Skater_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.SmartAssembly.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Spices_Net.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Xenocode.DeobfuscatorInfo(),
|
||||
};
|
||||
var dict = new Dictionary<string, IDeobfuscatorInfo>();
|
||||
foreach (var d in local)
|
||||
dict[d.Type] = d;
|
||||
string pluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin");
|
||||
GetPlugins(pluginDir, ref dict);
|
||||
return new List<IDeobfuscatorInfo>(dict.Values);
|
||||
}
|
||||
|
||||
public static int Main(string[] args) {
|
||||
int exitCode = 0;
|
||||
|
||||
const string showAllMessagesEnvName = "SHOWALLMESSAGES";
|
||||
try {
|
||||
if (Console.OutputEncoding.IsSingleByte)
|
||||
Console.OutputEncoding = new UTF8Encoding(false);
|
||||
|
||||
Logger.Instance.CanIgnoreMessages = !HasEnv(showAllMessagesEnvName);
|
||||
|
||||
Logger.n("");
|
||||
Logger.n("de4dot v{0} Copyright (C) 2011-2015 de4dot@gmail.com", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
|
||||
Logger.n("Latest version and source code: https://github.com/0xd4d/de4dot");
|
||||
Logger.n("");
|
||||
|
||||
var options = new FilesDeobfuscator.Options();
|
||||
ParseCommandLine(args, options);
|
||||
new FilesDeobfuscator(options).DoIt();
|
||||
}
|
||||
catch (ExitException ex) {
|
||||
exitCode = ex.code;
|
||||
}
|
||||
catch (UserException ex) {
|
||||
Logger.Instance.LogErrorDontIgnore("{0}", ex.Message);
|
||||
exitCode = 1;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (PrintFullStackTrace()) {
|
||||
PrintStackTrace(ex);
|
||||
Logger.Instance.LogErrorDontIgnore("\nTry the latest version!");
|
||||
}
|
||||
else {
|
||||
Logger.Instance.LogErrorDontIgnore("\n\n");
|
||||
Logger.Instance.LogErrorDontIgnore("Hmmmm... something didn't work. Try the latest version.");
|
||||
}
|
||||
exitCode = 1;
|
||||
}
|
||||
|
||||
if (Logger.Instance.NumIgnoredMessages > 0) {
|
||||
if (Logger.Instance.NumIgnoredMessages == 1)
|
||||
Logger.n("Ignored {0} warning/error", Logger.Instance.NumIgnoredMessages);
|
||||
else
|
||||
Logger.n("Ignored {0} warnings/errors", Logger.Instance.NumIgnoredMessages);
|
||||
Logger.n("Use -v/-vv option or set environment variable {0}=1 to see all messages", showAllMessagesEnvName);
|
||||
}
|
||||
|
||||
if (IsN00bUser()) {
|
||||
Console.Error.WriteLine("\n\nPress any key to exit...\n");
|
||||
try {
|
||||
Console.ReadKey(true);
|
||||
}
|
||||
catch (InvalidOperationException) {
|
||||
}
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
static bool PrintFullStackTrace() {
|
||||
if (!Logger.Instance.IgnoresEvent(LoggerEvent.Verbose))
|
||||
return true;
|
||||
if (HasEnv("STACKTRACE"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool HasEnv(string name) {
|
||||
foreach (var tmp in Environment.GetEnvironmentVariables().Keys) {
|
||||
var env = tmp as string;
|
||||
if (env == null)
|
||||
continue;
|
||||
if (string.Equals(env, name, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsN00bUser() {
|
||||
if (HasEnv("VisualStudioDir"))
|
||||
return false;
|
||||
if (HasEnv("SHELL"))
|
||||
return false;
|
||||
return HasEnv("windir") && !HasEnv("PROMPT");
|
||||
}
|
||||
|
||||
public static void PrintStackTrace(Exception ex) {
|
||||
PrintStackTrace(ex, LoggerEvent.Error);
|
||||
}
|
||||
|
||||
public static void PrintStackTrace(Exception ex, LoggerEvent loggerEvent) {
|
||||
var line = new string('-', 78);
|
||||
Logger.Instance.Log(false, null, loggerEvent, "\n\n");
|
||||
Logger.Instance.Log(false, null, loggerEvent, line);
|
||||
Logger.Instance.Log(false, null, loggerEvent, "Stack trace:\n{0}", ex.StackTrace);
|
||||
Logger.Instance.Log(false, null, loggerEvent, "\n\nCaught an exception:\n");
|
||||
Logger.Instance.Log(false, null, loggerEvent, line);
|
||||
Logger.Instance.Log(false, null, loggerEvent, "Message:");
|
||||
Logger.Instance.Log(false, null, loggerEvent, " {0}", ex.Message);
|
||||
Logger.Instance.Log(false, null, loggerEvent, "Type:");
|
||||
Logger.Instance.Log(false, null, loggerEvent, " {0}", ex.GetType());
|
||||
Logger.Instance.Log(false, null, loggerEvent, line);
|
||||
}
|
||||
|
||||
static void ParseCommandLine(string[] args, FilesDeobfuscator.Options options) {
|
||||
new CommandLineParser(deobfuscatorInfos, options).Parse(args);
|
||||
|
||||
Logger.vv("Args:");
|
||||
Logger.Instance.Indent();
|
||||
foreach (var arg in args)
|
||||
Logger.vv("{0}", Utils.ToCsharpString(arg));
|
||||
Logger.Instance.DeIndent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue