计算BasicBlock,并且计算是否属于loop中。支持const cache策略
parent
3f8f97207e
commit
3094532eaa
|
@ -0,0 +1,300 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Obfuz.Emit
|
||||
{
|
||||
public static class LoopDetector
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class BasicBlock
|
||||
{
|
||||
public readonly List<Instruction> instructions = new List<Instruction>();
|
||||
|
||||
public readonly List<BasicBlock> inBlocks = new List<BasicBlock>();
|
||||
|
||||
public readonly List<BasicBlock> outBlocks = new List<BasicBlock>();
|
||||
|
||||
public bool inLoop;
|
||||
|
||||
public void AddTargetBasicBlock(BasicBlock target)
|
||||
{
|
||||
if (!outBlocks.Contains(target))
|
||||
{
|
||||
outBlocks.Add(target);
|
||||
}
|
||||
if (!target.inBlocks.Contains(this))
|
||||
{
|
||||
target.inBlocks.Add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BasicBlockCollection
|
||||
{
|
||||
private readonly MethodDef _method;
|
||||
|
||||
private readonly List<BasicBlock> _blocks = new List<BasicBlock>();
|
||||
private readonly Dictionary<Instruction, BasicBlock> _inst2BlockMap = new Dictionary<Instruction, BasicBlock>();
|
||||
|
||||
public IList<BasicBlock> Blocks => _blocks;
|
||||
|
||||
public BasicBlockCollection(MethodDef method)
|
||||
{
|
||||
_method = method;
|
||||
HashSet<Instruction> splitPoints = BuildSplitPoint(method);
|
||||
BuildBasicBlocks(method, splitPoints);
|
||||
BuildInOutGraph(method);
|
||||
|
||||
var loopBlocks = FindLoopBlocks(_blocks);
|
||||
foreach (var block in loopBlocks)
|
||||
{
|
||||
block.inLoop = true;
|
||||
}
|
||||
}
|
||||
|
||||
public BasicBlock GetBasicBlockByInstruction(Instruction inst)
|
||||
{
|
||||
return _inst2BlockMap[inst];
|
||||
}
|
||||
|
||||
private HashSet<Instruction> BuildSplitPoint(MethodDef method)
|
||||
{
|
||||
var insts = method.Body.Instructions;
|
||||
var splitPoints = new HashSet<Instruction>();
|
||||
foreach (ExceptionHandler eh in method.Body.ExceptionHandlers)
|
||||
{
|
||||
if (eh.TryStart != null)
|
||||
{
|
||||
splitPoints.Add(eh.TryStart);
|
||||
}
|
||||
if (eh.TryEnd != null)
|
||||
{
|
||||
splitPoints.Add(eh.TryEnd);
|
||||
}
|
||||
if (eh.HandlerStart != null)
|
||||
{
|
||||
splitPoints.Add(eh.HandlerStart);
|
||||
}
|
||||
if (eh.HandlerEnd != null)
|
||||
{
|
||||
splitPoints.Add(eh.HandlerEnd);
|
||||
}
|
||||
if (eh.FilterStart != null)
|
||||
{
|
||||
splitPoints.Add(eh.FilterStart);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, n = insts.Count; i < n; i++)
|
||||
{
|
||||
Instruction curInst = insts[i];
|
||||
Instruction nextInst = i + 1 < n ? insts[i + 1] : null;
|
||||
switch (curInst.OpCode.FlowControl)
|
||||
{
|
||||
case FlowControl.Branch:
|
||||
{
|
||||
if (nextInst != null)
|
||||
{
|
||||
splitPoints.Add(nextInst);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FlowControl.Cond_Branch:
|
||||
{
|
||||
if (nextInst != null)
|
||||
{
|
||||
splitPoints.Add(nextInst);
|
||||
}
|
||||
if (curInst.Operand is Instruction targetInst)
|
||||
{
|
||||
splitPoints.Add(targetInst);
|
||||
}
|
||||
else if (curInst.Operand is Instruction[] targetInsts)
|
||||
{
|
||||
foreach (var target in targetInsts)
|
||||
{
|
||||
splitPoints.Add(target);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FlowControl.Return:
|
||||
{
|
||||
if (nextInst != null)
|
||||
{
|
||||
splitPoints.Add(nextInst);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FlowControl.Throw:
|
||||
{
|
||||
if (nextInst != null)
|
||||
{
|
||||
splitPoints.Add(nextInst);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return splitPoints;
|
||||
}
|
||||
|
||||
|
||||
private void BuildBasicBlocks(MethodDef method, HashSet<Instruction> splitPoints)
|
||||
{
|
||||
var insts = method.Body.Instructions;
|
||||
|
||||
|
||||
BasicBlock curBlock = new BasicBlock();
|
||||
foreach (Instruction inst in insts)
|
||||
{
|
||||
if (splitPoints.Contains(inst) && curBlock.instructions.Count > 0)
|
||||
{
|
||||
_blocks.Add(curBlock);
|
||||
curBlock = new BasicBlock();
|
||||
}
|
||||
curBlock.instructions.Add(inst);
|
||||
_inst2BlockMap.Add(inst, curBlock);
|
||||
}
|
||||
if (curBlock.instructions.Count > 0)
|
||||
{
|
||||
_blocks.Add(curBlock);
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildInOutGraph(MethodDef method)
|
||||
{
|
||||
var insts = method.Body.Instructions;
|
||||
for (int i = 0, n = _blocks.Count; i < n; i++)
|
||||
{
|
||||
BasicBlock curBlock = _blocks[i];
|
||||
BasicBlock nextBlock = i + 1 < n ? _blocks[i + 1] : null;
|
||||
Instruction lastInst = curBlock.instructions.Last();
|
||||
switch (lastInst.OpCode.FlowControl)
|
||||
{
|
||||
case FlowControl.Branch:
|
||||
{
|
||||
Instruction targetInst = (Instruction)lastInst.Operand;
|
||||
BasicBlock targetBlock = GetBasicBlockByInstruction(targetInst);
|
||||
curBlock.AddTargetBasicBlock(targetBlock);
|
||||
break;
|
||||
}
|
||||
case FlowControl.Cond_Branch:
|
||||
{
|
||||
if (lastInst.Operand is Instruction targetInst)
|
||||
{
|
||||
BasicBlock targetBlock = GetBasicBlockByInstruction(targetInst);
|
||||
curBlock.AddTargetBasicBlock(targetBlock);
|
||||
}
|
||||
else if (lastInst.Operand is Instruction[] targetInsts)
|
||||
{
|
||||
foreach (var target in targetInsts)
|
||||
{
|
||||
BasicBlock targetBlock = GetBasicBlockByInstruction(target);
|
||||
curBlock.AddTargetBasicBlock(targetBlock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Invalid operand type for conditional branch");
|
||||
}
|
||||
if (nextBlock != null)
|
||||
{
|
||||
curBlock.AddTargetBasicBlock(nextBlock);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FlowControl.Return:
|
||||
case FlowControl.Throw:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<BasicBlock> FindLoopBlocks(List<BasicBlock> allBlocks)
|
||||
{
|
||||
// Tarjan算法找强连通分量
|
||||
var sccList = FindStronglyConnectedComponents(allBlocks);
|
||||
|
||||
// 筛选有效循环
|
||||
var loopBlocks = new HashSet<BasicBlock>();
|
||||
foreach (var scc in sccList)
|
||||
{
|
||||
// 有效循环需满足以下条件之一:
|
||||
// 1. 分量包含多个块
|
||||
// 2. 单个块有自环(跳转自己)
|
||||
if (scc.Count > 1 ||
|
||||
(scc.Count == 1 && scc[0].outBlocks.Contains(scc[0])))
|
||||
{
|
||||
foreach (var block in scc)
|
||||
{
|
||||
loopBlocks.Add(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
return loopBlocks;
|
||||
}
|
||||
|
||||
private static List<List<BasicBlock>> FindStronglyConnectedComponents(List<BasicBlock> allBlocks)
|
||||
{
|
||||
int index = 0;
|
||||
var stack = new Stack<BasicBlock>();
|
||||
var indexes = new Dictionary<BasicBlock, int>();
|
||||
var lowLinks = new Dictionary<BasicBlock, int>();
|
||||
var onStack = new HashSet<BasicBlock>();
|
||||
var sccList = new List<List<BasicBlock>>();
|
||||
|
||||
foreach (var block in allBlocks.Where(b => !indexes.ContainsKey(b)))
|
||||
{
|
||||
StrongConnect(block);
|
||||
}
|
||||
|
||||
return sccList;
|
||||
|
||||
void StrongConnect(BasicBlock v)
|
||||
{
|
||||
indexes[v] = index;
|
||||
lowLinks[v] = index;
|
||||
index++;
|
||||
stack.Push(v);
|
||||
onStack.Add(v);
|
||||
|
||||
foreach (var w in v.outBlocks)
|
||||
{
|
||||
if (!indexes.ContainsKey(w))
|
||||
{
|
||||
StrongConnect(w);
|
||||
lowLinks[v] = System.Math.Min(lowLinks[v], lowLinks[w]);
|
||||
}
|
||||
else if (onStack.Contains(w))
|
||||
{
|
||||
lowLinks[v] = System.Math.Min(lowLinks[v], indexes[w]);
|
||||
}
|
||||
}
|
||||
|
||||
if (lowLinks[v] == indexes[v])
|
||||
{
|
||||
var scc = new List<BasicBlock>();
|
||||
BasicBlock w;
|
||||
do
|
||||
{
|
||||
w = stack.Pop();
|
||||
onStack.Remove(w);
|
||||
scc.Add(w);
|
||||
} while (!w.Equals(v));
|
||||
sccList.Add(scc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
using dnlib.DotNet.Emit;
|
||||
using dnlib.DotNet;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Obfuz.Emit;
|
||||
|
||||
namespace Obfuz.ObfusPasses
|
||||
{
|
||||
public abstract class BasicBlockObfuscationPassBase : ObfuscationPassBase
|
||||
{
|
||||
protected abstract bool NeedObfuscateMethod(MethodDef method);
|
||||
|
||||
public override void Process(ObfuscationPassContext ctx)
|
||||
{
|
||||
foreach (ModuleDef mod in ctx.toObfuscatedModules)
|
||||
{
|
||||
// ToArray to avoid modify list exception
|
||||
foreach (TypeDef type in mod.GetTypes().ToArray())
|
||||
{
|
||||
if (type.Name.StartsWith("$Obfuz$"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// ToArray to avoid modify list exception
|
||||
foreach (MethodDef method in type.Methods.ToArray())
|
||||
{
|
||||
if (!method.HasBody || method.Name.StartsWith("$Obfuz$") || !NeedObfuscateMethod(method))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// TODO if isGeneratedBy Obfuscator, continue
|
||||
ObfuscateData(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected abstract bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, BasicBlock block, int instructionIndex,
|
||||
List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions);
|
||||
|
||||
private void ObfuscateData(MethodDef method)
|
||||
{
|
||||
BasicBlockCollection bbc = new BasicBlockCollection(method);
|
||||
|
||||
IList<Instruction> instructions = method.Body.Instructions;
|
||||
|
||||
var outputInstructions = new List<Instruction>();
|
||||
var totalFinalInstructions = new List<Instruction>();
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
{
|
||||
Instruction inst = instructions[i];
|
||||
BasicBlock block = bbc.GetBasicBlockByInstruction(inst);
|
||||
outputInstructions.Clear();
|
||||
if (TryObfuscateInstruction(method, inst, block, i, outputInstructions, totalFinalInstructions))
|
||||
{
|
||||
// current instruction may be the target of control flow instruction, so we can't remove it directly.
|
||||
// we replace it with nop now, then remove it in CleanUpInstructionPass
|
||||
inst.OpCode = outputInstructions[0].OpCode;
|
||||
inst.Operand = outputInstructions[0].Operand;
|
||||
totalFinalInstructions.Add(inst);
|
||||
for (int k = 1; k < outputInstructions.Count; k++)
|
||||
{
|
||||
totalFinalInstructions.Add(outputInstructions[k]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
totalFinalInstructions.Add(inst);
|
||||
}
|
||||
}
|
||||
|
||||
instructions.Clear();
|
||||
foreach (var obInst in totalFinalInstructions)
|
||||
{
|
||||
instructions.Add(obInst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.ObfusPasses.ConstObfus.Policies;
|
||||
using Obfuz.Settings;
|
||||
using System;
|
||||
|
@ -13,7 +14,7 @@ using UnityEngine.Assertions;
|
|||
namespace Obfuz.ObfusPasses.ConstObfus
|
||||
{
|
||||
|
||||
public class ConstObfusPass : InstructionObfuscationPassBase
|
||||
public class ConstObfusPass : BasicBlockObfuscationPassBase
|
||||
{
|
||||
private readonly string _configFile;
|
||||
private IObfuscationPolicy _dataObfuscatorPolicy;
|
||||
|
@ -40,10 +41,10 @@ namespace Obfuz.ObfusPasses.ConstObfus
|
|||
return _dataObfuscatorPolicy.NeedObfuscateMethod(method);
|
||||
}
|
||||
|
||||
protected override bool TryObfuscateInstruction(MethodDef method, Instruction inst, IList<Instruction> instructions, int instructionIndex,
|
||||
protected override bool TryObfuscateInstruction(MethodDef method, Instruction inst, BasicBlock block, int instructionIndex,
|
||||
List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
|
||||
{
|
||||
bool currentInLoop = false;
|
||||
bool currentInLoop = block.inLoop;
|
||||
ConstCachePolicy constCachePolicy = _dataObfuscatorPolicy.GetMethodConstCachePolicy(method);
|
||||
switch (inst.OpCode.OperandType)
|
||||
{
|
||||
|
@ -127,7 +128,7 @@ namespace Obfuz.ObfusPasses.ConstObfus
|
|||
{
|
||||
if (((IMethod)inst.Operand).FullName == "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)")
|
||||
{
|
||||
Instruction prevInst = instructions[instructionIndex - 1];
|
||||
Instruction prevInst = block.instructions[instructionIndex - 1];
|
||||
if (prevInst.OpCode.Code == Code.Ldtoken)
|
||||
{
|
||||
IField rvaField = (IField)prevInst.Operand;
|
||||
|
|
Loading…
Reference in New Issue