[C#] 纯文本查看 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using de4dot.blocks;
using de4dot.blocks.cflow;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
namespace de4dot.code.deobfuscators.ConfuserEX {
class ControlFlow : IBlocksDeobfuscator {
private InstructionEmulator _emulator;
private Blocks _blocks;
private MethodDef _method;
private IList<Local> _locals;
private static readonly Code[] LDC_OPCODES = new Code[] { Code.Ldc_I4, Code.Ldc_I4_M1, Code.Ldc_I4_0, Code.Ldc_I4_1, Code.Ldc_I4_2, Code.Ldc_I4_3, Code.Ldc_I4_4, Code.Ldc_I4_5, Code.Ldc_I4_6, Code.Ldc_I4_7, Code.Ldc_I4_8, Code.Ldc_I4_S };
private static readonly Code[] LDLOC_OPCODES = new Code[] { Code.Ldloc, Code.Ldloc_0, Code.Ldloc_1, Code.Ldloc_2, Code.Ldloc_3, Code.Ldloc_S };
private static readonly Code[] STLOC_OPCODES = new Code[] { Code.Stloc, Code.Stloc_0, Code.Stloc_1, Code.Stloc_2, Code.Stloc_3, Code.Stloc_S };
private Dictionary<Block, List<Block>> _origSwitchTargets;
public bool ExecuteIfNotModified { get; } = false;
public void DeobfuscateBegin(Blocks blocks) {
_blocks = blocks;
_method = blocks.Method;
_locals = blocks.Locals;
_origSwitchTargets = new Dictionary<Block, List<Block>>();
_emulator = new InstructionEmulator();
_emulator.Initialize(blocks, false);
}
public bool Deobfuscate(List<Block> allBlocks) {
_method.Body.KeepOldMaxStack = true;
if (allBlocks.Count <= 0) {
return false;
}
var switchBlocks = new List<Block>();
foreach (var block in allBlocks) {
if (IsSwitchBlockStart(block)) {
switchBlocks.Add(block);
}
}
if (switchBlocks.Count <= 0) {
return false;
}
foreach (var switchBlock in switchBlocks) {
_origSwitchTargets.Add(switchBlock, new List<Block>(switchBlock.Targets));
}
DeFlattening(allBlocks, switchBlocks);
_method.Body.OptimizeBranches();
_method.Body.SimplifyBranches();
return true;
}
private void DeFlattening(List<Block> allBlocks, List<Block> switchBlocks) {
var toProcess = new List<Block>();
var savedLocals = new Dictionary<Block, List<Value>>();
// Find the first switch
var firstFreshSwitchTuple = FallThroughAndFindFirstSwitch(null, allBlocks[0], 0);
// Find the first switch case (assume the default case is unused)
var firstCase = FallThroughNextCase(firstFreshSwitchTuple.Item2);
// Redirect to the first case
DisconnectFromSwitch(firstCase);
firstFreshSwitchTuple.Item1.ReplaceLastNonBranchWithBranch(0, firstCase);
var handlers = new List<Block>();
foreach (var handler in _method.Body.ExceptionHandlers) {
foreach (var block in allBlocks) {
if (block.FirstInstr.Instruction.Equals(handler.HandlerStart)) {
handlers.Add(block);
break;
}
}
}
var locals = SaveEmulatorLocals();
foreach (var handlerBlock in handlers) {
var handlerFirstFreshSwitchTuple = FallThroughAndFindFirstSwitch(null, handlerBlock, 0);
// Find the first switch case of the handler (assume the default case is unused)
var nextCase = FallThroughNextCase(handlerFirstFreshSwitchTuple.Item2);
DisconnectFromSwitch(nextCase);
handlerFirstFreshSwitchTuple.Item1.ReplaceLastNonBranchWithBranch(0, nextCase);
toProcess.Add(nextCase);
savedLocals.Add(nextCase, locals);
}
toProcess.Add(firstCase);
while (toProcess.Count > 0) {
var curBlock = toProcess[toProcess.Count - 1];
toProcess.Remove(curBlock);
if (savedLocals.ContainsKey(curBlock)) {
RestoreEmulatorLocals(savedLocals[curBlock]);
savedLocals.Remove(curBlock);
}
// Redirect to the next case
var nextCase = FallThroughNextCase(curBlock);
Cleanup(curBlock);
DisconnectFromSwitch(nextCase);
if (curBlock.IsFallThrough()) {
curBlock.ReplaceLastNonBranchWithBranch(0, nextCase);
}
else if (IsLeave(curBlock.LastInstr)) {
curBlock.Targets[0] = nextCase;
}
else {
throw new Exception("DeFlattening: Unexpected (0x1)");
}
if (nextCase.LastInstr.OpCode.Code != Code.Ret && !handleBranch(toProcess, savedLocals, nextCase)) {
toProcess.Add(nextCase);
}
}
}
private bool handleBranch(List<Block> toProcess, Dictionary<Block, List<Value>> savedLocals, Block block) {
if (block.IsConditionalBranch()) {
var falseBr = block.FallThrough;
var trueBr = block.Targets[0];
if (!handleBranch(toProcess, savedLocals, trueBr)) {
toProcess.Add(trueBr);
savedLocals.Add(trueBr, SaveEmulatorLocals());
}
if (!handleBranch(toProcess, savedLocals, falseBr)) {
toProcess.Add(falseBr);
savedLocals.Add(falseBr, SaveEmulatorLocals());
}
return true;
}
else if (!IsSwitchBlockStart(block) && block.LastInstr.OpCode.Code == Code.Switch) {
foreach (var target in block.Targets) {
if (!handleBranch(toProcess, savedLocals, target)) {
toProcess.Add(target);
savedLocals.Add(target, SaveEmulatorLocals());
}
}
// Switch default
var defaultCase = block.FallThrough;
toProcess.Add(defaultCase);
savedLocals.Add(defaultCase, SaveEmulatorLocals());
return true;
}
return false;
}
private void DisconnectFromSwitch(Block caseBlock) {
if (caseBlock.Sources != null) {
var toRemove = new List<Block>();
foreach (var source in caseBlock.Sources) {
if (IsSwitchBlockFreshStart(source)) {
toRemove.Add(source);
}
else if (IsSwitchBlockStart(source)) {
toRemove.Add(source);
source.Targets.Remove(caseBlock);
}
}
foreach (var block in toRemove) {
caseBlock.Sources.Remove(block);
}
}
}
// Remove useless instrs
private void Cleanup(Block caseBlock) {
if (caseBlock.IsFallThrough() && IsSwitchBlockStart(caseBlock.FallThrough)) {
if (IsCalculateSwitchValue(caseBlock)) {
caseBlock.Instructions.RemoveRange(caseBlock.Instructions.Count - 5, 5);
}
else if (IsConstantSwitchValue(caseBlock)) {
caseBlock.Instructions.Remove(caseBlock.LastInstr);
}
}
}
// Return next switch case
private Block FallThroughNextCase(Block curBlock) {
if (IsSwitchBlockFreshStart(curBlock)) {
var nextCase = GetCase(curBlock, GetCaseValueFromFreshSwitch(curBlock).Value);
if (IsSwitchBlockStart(nextCase)) {
throw new Exception("FallThroughNextCase: Unexpected (0x1)");
}
else if (IsSwitchBlockFreshStart(nextCase)) {
nextCase = FallThroughNextCase(nextCase);
}
return nextCase;
}
// If the next branch is a switch block
else if (curBlock.IsFallThrough() && (IsSwitchBlockFreshStart(curBlock.FallThrough) || IsSwitchBlockStart(curBlock.FallThrough))) {
if (IsSwitchBlockFreshStart(curBlock.FallThrough)) {
return FallThroughNextCase(curBlock.FallThrough);
}
else {
// Calculate the stack value
_emulator.Emulate(curBlock.Instructions);
var nextCase = GetCase(curBlock.FallThrough, GetCaseValueFromSwitch(curBlock.FallThrough, (Int32Value)_emulator.Pop()).Value);
if (IsSwitchBlockStart(nextCase)) {
throw new Exception("FallThroughNextCase: Unexpected (0x2)");
}
else if (IsSwitchBlockFreshStart(nextCase)) {
nextCase = FallThroughNextCase(nextCase);
}
return nextCase;
}
}
else if (curBlock.IsFallThrough()) {
return curBlock.FallThrough;
}
else if (IsLeave(curBlock.LastInstr)) {
return curBlock.Targets[0];
}
else {
throw new Exception("FallThroughNextCase: Unexpected (0x3)");
}
}
private bool IsLeave(Instr instr) {
return instr.OpCode.Code == Code.Leave || instr.OpCode.Code == Code.Leave_S;
}
// prev switch
private Tuple<Block, Block> FallThroughAndFindFirstSwitch(Block prev, Block block, int depth) {
if (depth >= 30) {
throw new Exception("Overflow");
}
else if (block.IsConditionalBranch() || !block.IsFallThrough()) {
throw new Exception("Conditional branch not supported yet");
}
else if (IsSwitchBlockStart(block) || block.LastInstr.OpCode.Code == Code.Ret) {
throw new Exception("Unexpected state");
}
if (IsSwitchBlockFreshStart(block)) {
return new Tuple<Block, Block> {
Item1 = prev,
Item2 = block,
};
}
else {
return FallThroughAndFindFirstSwitch(block, block.FallThrough, depth++);
}
}
private Int32Value GetCaseValueFromFreshSwitch(Block switchBlock) {
if (!switchBlock.IsFallThrough()) {
throw new Exception("GetCaseValueFromFreshSwitch: Invalid switch (0x1)");
}
else if (!IsSwitchBlockFreshStart(switchBlock)) {
throw new Exception("GetCaseValueFromFreshSwitch: Invalid switch (0x2)");
}
var instrs = new List<Instr>();
// Copy switch block's instrs but skip switch instruction
instrs.AddRange(switchBlock.Instructions);
instrs.AddRange(switchBlock.FallThrough.Instructions.GetRange(0, switchBlock.FallThrough.Instructions.Count - 1));
_emulator.Emulate(instrs);
return (Int32Value)_emulator.Pop();
}
private Int32Value GetCaseValueFromSwitch(Block switchBlock, Int32Value stackValue) {
if (!IsSwitchBlockStart(switchBlock)) {
throw new Exception("GetCaseValueFromFreshSwitch: Invalid switch (0x1)");
}
_emulator.Push(stackValue);
// Copy switch block's instrs but skip switch instruction
_emulator.Emulate(switchBlock.Instructions.GetRange(0, switchBlock.Instructions.Count - 1));
return (Int32Value)_emulator.Pop();
}
private Block GetCase(Block switchBlock, int key) {
if (IsSwitchBlockFreshStart(switchBlock)) {
return _origSwitchTargets[switchBlock.FallThrough][key];
}
else if (IsSwitchBlockStart(switchBlock)) {
return _origSwitchTargets[switchBlock][key];
}
else {
throw new Exception("GetCase: Invalid switch");
}
}
private bool IsSwitchBlockFreshStart(Block block) {
if (!block.IsFallThrough()) {
return false;
}
var nextBlock = block.FallThrough;
return block.Instructions.Count == 1 && block.Instructions[0].OpCode.Code == Code.Ldc_I4 && IsSwitchBlockStart(nextBlock);
}
private bool IsSwitchBlockStart(Block block) {
var pattern = new List<object>
{
Code.Ldc_I4,
Code.Xor,
Code.Dup,
Code.Stloc,
Code.Ldc_I4,
Code.Rem_Un,
Code.Switch
};
return OpcodeMatch(block, pattern);
}
private bool IsCalculateSwitchValue(Block block) {
var pattern = new List<object>
{
Code.Ldloc,
Code.Ldc_I4,
Code.Mul,
Code.Ldc_I4,
Code.Xor,
};
return OpcodeMatch(block, pattern);
}
/*
* ldc.i4 ...
* br switch
*/
private bool IsConstantSwitchValue(Block block) {
return block.Instructions.Count > 0 && block.LastInstr.IsLdcI4() && block.IsFallThrough() && IsSwitchBlockStart(block.FallThrough);
}
private bool OpcodeMatch(Block block, List<object> opcodes) {
if (block.Instructions.Count < opcodes.Count) {
return false;
}
for (var i = 0; i < opcodes.Count; i++) {
object opcode = opcodes[i];
if (!opcode.GetType().IsArray) {
if ((Code)opcode == Code.Ldc_I4) {
opcodes[i] = LDC_OPCODES;
}
else if ((Code)opcode == Code.Stloc) {
opcodes[i] = STLOC_OPCODES;
}
else if ((Code)opcode == Code.Ldloc) {
opcodes[i] = LDLOC_OPCODES;
}
}
}
var idx = block.Instructions.IndexOf(block.LastInstr);
for (var i = opcodes.Count - 1; i >= 0; i--, idx--) {
var instr = block.Instructions[idx];
if (opcodes[i].GetType().IsArray) {
bool found = false;
foreach (var opcode in (Code[])opcodes[i]) {
if (instr.OpCode.Code == opcode) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
else if (instr.OpCode.Code != (Code)opcodes[i]) {
return false;
}
}
return true;
}
private List<Value> SaveEmulatorLocals() {
var locals = new List<Value>();
for (var i = 0; i < _locals.Count; i++) {
locals.Add(_emulator.GetLocal(i));
}
return locals;
}
private void RestoreEmulatorLocals(List<Value> locals) {
for (var i = 0; i < locals.Count; i++) {
_emulator.SetLocal(_locals[i], locals[i]);
}
}
}
}