程序保护实战系列 09 · 反调试与反转储
攻击者的两大利器攻击方式 工具 目标动态调试 dnSpy、Windbg、x64dbg 单步执行、修改变量、修改逻辑内存转储 ProcessHacker、MegaDumper 提取解密后的 IL、字符串、密钥2. AntiDebug托管 原生双检测internal static class AntiDebug{public static void Initialize(){// 方式1托管调试器检测Visual Studio、dnSpy 托管调试if (Debugger.IsAttached)Environment.Exit(-1);// 方式2原生调试器检测Windbg、x64dbg、OllyDbg try { if (IsDebuggerPresent()) Environment.Exit(-1); } catch { } // 非 Windows 平台不调用此 API } [DllImport(kernel32.dll)] static extern bool IsDebuggerPresent();}关联的 AdditionalProtectionsStep 将 AntiDebug.Initialize() 插入 …cctor()确保在任何用户代码之前执行。AntiDumpunsafe 指针清零 PE 信息MegaDumper 等工具通过读取内存中的 PE 头重建程序集文件。我们的对策是直接清零 PE 关键结构internal static unsafe class AntiDump{public static void Initialize(){// 1. 获取模块基址 — 通过 GetModuleHandlevar module GetModuleHandle(null);// 2. 解析 PE 头结构 byte* basePtr (byte*)module; var dosHeader (IMAGE_DOS_HEADER*)basePtr; var peOffset dosHeader-e_lfanew; var ntHeaders (IMAGE_NT_HEADERS*)(basePtr peOffset); var sections (IMAGE_SECTION_HEADER*)( basePtr peOffset sizeof(IMAGE_NT_HEADERS) - // 其实是 sizeof(IMAGE_NT_HEADERS64) sizeof(IMAGE_OPTIONAL_HEADER64) ntHeaders-FileHeader.SizeOfOptionalHeader); // 3. 清零 PE 节区名称每个节区 Name 字段 8 字节 for (int i 0; i ntHeaders-FileHeader.NumberOfSections; i) *(ulong*)sections[i].Name 0; // ILT\0\0\0\0 → \0\0\0\0\0\0\0\0 // 4. 清零 CLR 元数据目录 var corHeader GetCorHeader(basePtr, ntHeaders); *(uint*)corHeader-cb 0; // 大小清零 *(uint*)corHeader-MajorRuntimeVersion 0; *(uint*)corHeader-MinorRuntimeVersion 0; // 5. 清零元数据流名称 // #~, #Strings, #US, #GUID, #Blob 流名称统一清零 var metaData GetMetadataRoot(corHeader); for (int i 0; i metaData-StreamHeaders.Streams; i) *(ulong*)metaData-StreamHeaders.Stream[i].Name 0; // 6. 修改导入表中的 VirtualProtect 调用痕迹 var importTable GetImportTable(basePtr, ntHeaders); for (int i 0; i importTable-Count; i) { importTable-Entries[i].ModuleName[0] ?; // 隐藏模块名 importTable-Entries[i].FuncName[0] ?; // 隐藏函数名 } }}关键使用 GetModuleHandle 替代 Marshal.GetHINSTANCE避免 .NET 版本跨平台兼容性问题。元数据混淆虚假 TypeDef MVID 修改// 添加 5-8 个虚假 TypeDefint count random.Next(5, 9);for (int i 0; i count; i){var fakeType new TypeDefUser(“”, // 空命名空间GenerateInvisibleName(random), // 不可见 Unicode 名称module.CorLibTypes.Object.TypeDefOrRef);fakeType.Attributes TypeAttributes.Public | TypeAttributes.Class;// 每个虚假类型添加 1-2 个空方法int methodCount random.Next(1, 3);for (int j 0; j methodCount; j){var method new MethodDefUser(GenerateInvisibleName(random),MethodSig.CreateStatic(module.CorLibTypes.Void),MethodAttributes.Public | MethodAttributes.Static);method.Body new CilBody();method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));fakeType.Methods.Add(method);}module.Types.Add(fakeType);}// 修改 MVID 破坏反编译器缓存module.Mvid Guid.NewGuid();5. AdditionalProtectionsStep四项保护统筹public class AdditionalProtectionsStep : IProtectionStep{public string Name “其它推荐保护”;public int Order 40;public bool IsEnabled(ProtectOptions options) options.Additional; public void Inject(ProtectionContext context) { // 根据配置选择性注入 if (context.Options.AntiDebug) InjectAntiDebug(context); // AntiDebug 类型 if (context.Options.AntiDump) InjectAntiDump(context); // AntiDump 类型 if (context.Options.EncryptResources) EncryptResources(context); // SPN 三阶段加密详见第10篇 if (context.Options.MetadataConfusion) InjectFakeTypes(context); // 虚假 TypeDef MVID 修改 } public void Execute(ProtectionContext context) { // 在 Module.cctor 开头插入初始化调用 InsertCctorCalls(context.Module, new[] { context.Options.AntiDebug ? AntiDebug.Initialize : null, context.Options.AntiDump ? AntiDump.Initialize : null, context.Options.EncryptResources ? ResourceEncryptor.Initialize : null, }); }}6. 结语