09 · 反调试与反转储
目录
- 攻击者的两大利器(#1-攻击者的两大利器)
- AntiDebug:托管 + 原生双检测(#2-antidebug托管--原生双检测)
- AntiDump:unsafe 指针清零 PE 信息(#3-antidumpunsafe-指针清零-pe-信息)
- 元数据混淆:虚假 TypeDef + MVID 修改(#4-元数据混淆虚假-typedef--mvid-修改)
- AdditionalProtectionsStep:四项保护统筹(#5-additionalprotectionsstep四项保护统筹)
- 结语(#6-结语)
1. 攻击者的两大利器
| 攻击方式 | 工具 | 目标 |
|----------|------|------|
| 动态调试 | dnSpy、Windbg、x64dbg | 单步执行、修改变量、修改逻辑 |
| 内存转储 | ProcessHacker、MegaDumper | 提取解密后的 IL、字符串、密钥 |
2. AntiDebug:托管 + 原生双检测
```csharp
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()` 插入 `<Module>..cctor()`,确保在任何用户代码之前执行。
3. AntiDump:unsafe 指针清零 PE 信息
MegaDumper 等工具通过读取内存中的 PE 头重建程序集文件。我们的对策是直接清零 PE 关键结构:
```csharp
internal static unsafe class AntiDump
{
public static void Initialize()
{
// 1. 获取模块基址 --- 通过 GetModuleHandle
var 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*)sectionsi.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.Streami.Name = 0;
// 6. 修改导入表中的 VirtualProtect 调用痕迹
var importTable = GetImportTable(basePtr, ntHeaders);
for (int i = 0; i < importTable->Count; i++)
{
importTable->Entriesi.ModuleName0 = '?'; // 隐藏模块名
importTable->Entriesi.FuncName0 = '?'; // 隐藏函数名
}
}
}
```
关键:使用 `GetModuleHandle` 替代 `Marshal.GetHINSTANCE`,避免 .NET 版本跨平台兼容性问题。
4. 元数据混淆:虚假 TypeDef + MVID 修改
```csharp
// 添加 5-8 个虚假 TypeDef
int 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:四项保护统筹
```csharp
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. 结语
反调试和反转储是动态防御的核心。对抗有经验的逆向工程师需要层层设防------从调试器检测到内存清除再到元数据混淆。这些技术与之前文章中的静态保护形成完整的立体防护。
下一篇将讲解**资源加密**------如何保护程序集中嵌入的 DLL、图片和配置文件。
> 本文由 TWSoft.AssemblyProtector 驱动。完整源码和工具请访问项目主页。