1. 什么是控制流混淆
正常方法:A → B → C → D → E
混淆后:A → jump → C → jump → D → junk → jump → B → jump → E → ...
2. BlockParser:将线性 IL 解析为块树
internal static class BlockParser
{
public static ScopeBlock ParseBody(CilBody body)
{
var root = new ScopeBlock();
foreach (var eh in body.ExceptionHandlers)
{
var tryBlock = new ScopeBlock();
RegisterBlock(tryBlock, eh.TryStart, eh.TryEnd);
root.Children.Add(tryBlock);
var handlerBlock = new ScopeBlock();
RegisterBlock(handlerBlock, eh.HandlerStart, eh.HandlerEnd);
root.Children.Add(handlerBlock);
}
Block currentBlock = null;
foreach (var instr in body.Instructions)
{
if (IsBlockBoundary(instr))
{
currentBlock = new Block();
GetEnclosingScope(currentBlock, instr).Children.Add(currentBlock);
}
currentBlock?.Instructions.Add(instr);
}
return root;
}
}
3. JumpMangler:拆分、跳转、打乱
public class JumpMangler
{
public void Mangle(CilBody body, ScopeBlock root, CFContext ctx)
{
body.MaxStack = (ushort)(body.MaxStack + 2);
foreach (var block in GetAllBlocks(root))
{
var fragments = SplitFragments(block, ctx);
if (fragments.Count < 4) continue;
foreach (var frag in fragments)
{
frag.Add(Instruction.Create(OpCodes.Br, nextFrag.Start));
InsertJunkInstructions(frag, ctx);
}
var first = fragments.First; fragments.RemoveFirst();
var last = fragments.Last; fragments.RemoveLast();
var middle = fragments.ToList();
ctx.Random.Shuffle(middle);
block.Instructions = Rebuild(first, middle, last);
}
FixEHOffsets(body);
}
List<Fragment> SplitFragments(Block block, CFContext ctx)
{
foreach (var instr in block.Instructions)
{
if (instr.OpCode.OpCodeType == OpCodeType.Prefix) continue;
currentFragment.Add(instr);
if (ctx.Random.NextDouble() < intensity && currentFragment.Count >= 2)
{
fragments.Add(currentFragment);
currentFragment = new Fragment();
}
}
}
}
4. 不透明谓词与垃圾指令
Dictionary<PredicateType, Func<CilBody, Instruction[]>> _predicates = new()
{
[PredicateType.FalseTrue] = body =>
{
body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_0));
body.Instructions.Add(Instruction.Create(OpCodes.Brtrue, junkLabel));
body.Instructions.Add(junkLabel);
},
[PredicateType.TrueFalse] = body =>
{
body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_1));
body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, junkLabel));
body.Instructions.Add(junkLabel);
},
[PredicateType.XorIdentity] = body =>
{
int key1 = random.Next(), key2 = random.Next();
int expected = key1 ^ key2;
body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4, key1));
body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4, key2));
body.Instructions.Add(Instruction.Create(OpCodes.Xor));
body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4, expected));
body.Instructions.Add(Instruction.Create(OpCodes.Ceq));
body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, junkLabel));
body.Instructions.Add(junkLabel);
}
};
每次保护的 key 随机生成,使每个混淆后的程序互不相同。
5. Debug 构建的自动兼容
static bool IsDisableOptimizations(CustomAttribute attr)
{
const int DisableOptimizations = 0x100;
int modes = (int)attr.ConstructorArguments[0].Value;
return (modes & DisableOptimizations) != 0;
}
if (IsDisableOptimizations(debugAttr))
{
context.Log("Debug 构建检测到,跳过控制流混淆");
return;
}
6. 异常处理边界的修复
混淆后的指令流需要更新所有异常处理器的边界:
static void FixEHOffsets(CilBody body, List<Instruction> newInstrs)
{
foreach (var eh in body.ExceptionHandlers)
{
var oldTryStart = eh.TryStart;
var oldTryEnd = eh.TryEnd;
eh.TryStart = newInstrs.First(i => i.SequencePoint?.Offset == oldTryStart.Offset);
eh.TryEnd = newInstrs.First(i => i.SequencePoint?.Offset == oldTryEnd.Offset);
}
}
7. 完整混淆流程
static void ProcessMethod(CilBody body, CFContext ctx)
{
if (MaxStackCalculator.GetMaxStack(body.Instructions,
body.ExceptionHandlers, out uint maxStack))
body.MaxStack = (ushort)maxStack;
var root = BlockParser.ParseBody(body);
var mangler = new JumpMangler();
mangler.Mangle(body, root, ctx);
body.Instructions.Clear();
root.ToBody(body);
foreach (var instr in body.Instructions)
{
if (instr.Operand is Instruction target)
instr.Operand = newInstrMap[target];
}
}
8. 结语
控制流混淆是攻击者最头疼的保护层之一——即使反编译出 C# 代码,得到的也是满屏的 goto 和垃圾逻辑。配合字符串加密和符号混淆,三层叠加后基本无法通过静态分析理解代码逻辑。
下一篇将深入代码虚拟化——另一种更高强度的保护策略。
本文由 TWSoft.AssemblyProtector 驱动。完整源码和工具请访问项目主页。
转自https://www.cnblogs.com/gsyifan/p/21163151
该文章在 2026/7/10 8:19:08 编辑过