11 · 类型注入:无 DLL 依赖的运行时交付
目录
- 设计哲学:零外部依赖
- [InjectHelper:dnlib 深度克隆引擎](#InjectHelper:dnlib 深度克隆引擎)
- [InjectContext:ImportMapper 与框架重定向](#InjectContext:ImportMapper 与框架重定向)
- MutationHelper:占位符替换引擎
- RuntimeService:从保护工具自身加载运行时
- 完整注入流程示例
- 结语
1. 设计哲学:零外部依赖
许多保护工具要求用户分发运行时 DLL(如 VMProtectSDK32.dll)。我们的设计:所有运行时类型在保护时完整克隆到目标程序集。
2. InjectHelper:dnlib 深度克隆引擎
csharp
public static class InjectHelper
{
// === 入口 1:克隆整个类型到目标模块 ===
public static TypeDef Inject(TypeDef typeDef, ModuleDef target)
{
var ctx = new InjectContext(typeDef.Module, target);
return (TypeDef)ctx.Import(typeDef);
}
// === 入口 2:克隆单个方法 ===
public static MethodDef Inject(MethodDef methodDef, ModuleDef target)
{
var ctx = new InjectContext(methodDef.Module, target);
return (MethodDef)ctx.Import(methodDef);
}
// === 入口 3:批量克隆(共享上下文,处理交叉引用) ===
public static List<TypeDef> InjectMany(
IEnumerable<TypeDef> typeDefs, ModuleDef target)
{
var ctx = new InjectContext(typeDefs.First().Module, target);
return typeDefs.Select(t => (TypeDef)ctx.Import(t)).ToList();
}
}
克隆管道:浅拷贝 → 深拷贝 → 签名修复
csharp
class InjectContext : ImportMapper
{
// 映射表:origin → clone
Dictionary<IMemberRef, IMemberRef> _map = new();
IMemberRef Import(IMemberRef member)
{
// 1. 已克隆?→ 直接返回映射
if (_map.TryGetValue(member, out var mapped))
return mapped;
// 2. 根据类型深度克隆
return member switch
{
TypeDef td => Clone(td),
MethodDef md => Clone(md),
FieldDef fd => Clone(fd),
TypeRef tr => TargetModule.Import(tr),
MemberRef mr => TargetModule.Import(mr),
_ => member
};
}
TypeDef Clone(TypeDef origin)
{
var clone = new TypeDefUser(origin.Namespace, origin.Name)
{
Attributes = origin.Attributes,
BaseType = Import(origin.BaseType)
};
// 记录映射
_map[origin] = clone;
// 克隆接口
foreach (var iface in origin.Interfaces)
clone.Interfaces.Add(new InterfaceImplUser(Import(iface.Interface)));
// 克隆属性
foreach (var prop in origin.Properties)
clone.Properties.Add(Clone(prop));
// 克隆字段
foreach (var field in origin.Fields)
clone.Fields.Add(Clone(field));
// 克隆方法(含方法体 IL)
foreach (var method in origin.Methods)
clone.Methods.Add(Clone(method));
return clone;
}
MethodDef Clone(MethodDef origin)
{
var clone = new MethodDefUser(
origin.Name,
Import(origin.MethodSig),
origin.ImplAttributes,
origin.Attributes);
_map[origin] = clone;
// 克隆方法体(IL 指令、局部变量、异常处理器)
if (origin.HasBody)
{
clone.Body = new CilBody();
clone.Body.InitLocals = origin.Body.InitLocals;
clone.Body.MaxStack = origin.Body.MaxStack;
// 复制局部变量
foreach (var local in origin.Body.Variables)
clone.Body.Variables.Add(new Local(Import(local.Type)));
// 复制 IL 指令(映射操作数)
foreach (var instr in origin.Body.Instructions)
{
var cloneInstr = new Instruction(
instr.OpCode,
MapOperand(instr.Operand));
clone.Body.Instructions.Add(cloneInstr);
}
// 复制异常处理器
foreach (var eh in origin.Body.ExceptionHandlers)
{
clone.Body.ExceptionHandlers.Add(new ExceptionHandler(eh.HandlerType)
{
CatchType = Import(eh.CatchType),
TryStart = Map(eh.TryStart),
TryEnd = Map(eh.TryEnd),
HandlerStart = Map(eh.HandlerStart),
HandlerEnd = Map(eh.HandlerEnd),
});
}
}
return clone;
}
}
3. InjectContext:ImportMapper 与框架重定向
.NET Core 的 UI 外观程序集(System.Runtime.dll 等)需要重定向到核心库:
csharp
public override IMemberRef Map(IMemberRef member)
{
var asm = member.DeclaringType?.DefinitionAssembly;
if (asm == null) return base.Map(member);
// 0a. .NET Framework 目标遇到 System.Private.CoreLib → 重定向到 mscorlib
if (!TargetIsNetCore && IsCoreLib(asm))
return RedirectTo(asm, TargetModule.CorLibTypes.AssemblyRef);
// 0. 外观程序集 → 重定向到 System.Private.CoreLib 或 mscorlib
if (IsFacadeAssembly(asm))
{
var corlib = TargetModule.CorLibTypes.AssemblyRef;
return RedirectTo(asm, corlib);
}
// 1. 目标模块已有同名引用?→ 使用已有的
var existingRef = TargetModule.GetAssemblyRef(asm.Name);
if (existingRef != null && existingRef.Version == asm.Version)
return existingRef;
return base.Map(member);
}
static readonly HashSet<string> _facadeAsmNames = new()
{
"System.Runtime", "System.IO", "System.Linq", "System.Collections",
"System.Threading", "System.Reflection", "System.Diagnostics",
"netstandard", "System.Net.Http", "System.ComponentModel",
// ... 40+ 个框架外观程序集
};
4. MutationHelper:占位符替换引擎
csharp
public static class MutationHelper
{
// 映射:Mutation 字段名 → key 索引
static readonly Dictionary<string, int> field2index = new()
{
{ "KeyI0", 0 }, { "KeyI1", 1 }, { "KeyI2", 2 }, { "KeyI3", 3 },
{ "KeyI4", 4 }, { "KeyI5", 5 }, { "KeyI6", 6 }, { "KeyI7", 7 },
{ "KeyI8", 8 }, { "KeyI9", 9 }, { "KeyI10", 10 }, { "KeyI11", 11 },
{ "KeyI12", 12 }, { "KeyI13", 13 }, { "KeyI14", 14 }, { "KeyI15", 15 },
};
// 替换 Key 占位符
public static void InjectKey(MethodDef method, int keyId, int value)
{
foreach (var instr in method.Body.Instructions)
{
if (instr.OpCode != OpCodes.Ldsfld) continue;
var field = (IField)instr.Operand;
// TWSoft.Runtime.Mutation.KeyI0 → ldc.i4 <value>
if (field.DeclaringType.Name == "Mutation" &&
field2index.TryGetValue(field.Name, out int id) && id == keyId)
{
instr.OpCode = OpCodes.Ldc_I4;
instr.Operand = value;
}
}
}
// 替换 Placeholder 调用
public static void ReplacePlaceholder(
MethodDef method,
Func<Instruction[], Instruction[]> repl)
{
foreach (var instr in method.Body.Instructions.ToList())
{
if (instr.OpCode != OpCodes.Call) continue;
var called = (IMethod)instr.Operand;
// Mutation.Placeholder<T>(T val) → 替换为真实指令序列
if (called.Name == "Placeholder")
{
// 追踪参数来源
Instruction[] argInstructions = TraceArguments(instr);
// 移除原 call 指令
int index = method.Body.Instructions.IndexOf(instr);
method.Body.Instructions.RemoveAt(index);
// 插入替换指令
var replacement = repl(argInstructions);
foreach (var replInstr in replacement.Reverse())
method.Body.Instructions.Insert(index, replInstr);
}
}
}
}
5. RuntimeService:从保护工具自身加载运行时
csharp
public sealed class RuntimeService
{
ModuleDef? _rtModule;
public TypeDef GetRuntimeType(string fullName)
{
_rtModule ??= LoadSelfModule(); // 首次调用懒加载
return _rtModule.Find(fullName, true)
?? throw new InvalidOperationException(
$"Runtime type '{fullName}' not found");
}
static ModuleDef LoadSelfModule()
{
var asm = typeof(RuntimeService).Assembly;
return ModuleDefMD.Load(asm.Location,
new ModuleCreationOptions { TryToLoadPdbFromDisk = false });
}
}
使用示例:
csharp
var runtime = context.Runtime; // 从 ProtectionContext 获取
var constantType = runtime.GetRuntimeType("TWSoft.Runtime.Constant");
var clonedConstant = InjectHelper.Inject(constantType, context.Module); // 克隆
6. 完整注入流程示例
以字符串加密为例,完整注入流程:
csharp
// Step 1: 获取运行时类型模板
var constantType = context.Runtime.GetRuntimeType("TWSoft.Runtime.Constant");
// Step 2: 深度克隆到目标模块
var clonedType = InjectHelper.Inject(constantType, context.Module);
// Step 3: 替换占位符为真实值
var initMethod = clonedType.FindMethod("Initialize");
MutationHelper.InjectKey(initMethod, 0, bufferLength); // KeyI0 → 真实长度
MutationHelper.InjectKey(initMethod, 1, seed); // KeyI1 → 真实种子
MutationHelper.ReplacePlaceholder(initMethod, args => // Placeholder → 数组初始化 IL
CreateArrayInitInstructions(args));
// Step 4: 重命名注入成员为不可见 Unicode
foreach (var method in clonedType.Methods)
method.Name = GenerateInvisibleName(random);
// Step 5: 标记不可被混淆
MarkAsNotRenameable(clonedType);
// Step 6: 插入 <Module>.cctor 初始化调用
InsertCctorCall(context.Module, clonedType, "Initialize");
7. 结语
InjectHelper + MutationHelper + RuntimeService 构成了整个保护引擎的基础设施。所有运行时类型(Constant、MethodGuard、VirtualMachine、AntiTamper、AntiDebug、AntiDump、ResourceEncryptor)都通过这条管道交付给目标程序集------零外部依赖、完全自包含。
下一篇(最后一篇)将讲解授权保护集成------如何将商业授权验证嵌入保护流程。
本文由 TWSoft.AssemblyProtector 驱动。完整源码和工具请访问项目主页。