第49章 注册表逆向:注册表操作监控、注册表API Hook、注册表保护

注册表这东西,Windows系统的核心命脉。说它是操作系统的"神经中枢"一点不为过。我做了这么多年逆向,跟注册表打交道的次数数都数不清。每次分析恶意软件,十有八九都能看到它对注册表动手动脚。

这一章,咱们就聊聊注册表逆向的三个核心话题:怎么监控注册表操作、怎么Hook注册表API、以及怎么保护注册表不被乱改。嗯,都是实战中经常碰到的活儿。

注册表操作监控:先看看谁在搞事情

监控注册表操作,说白了就是搞清楚"谁、在什么时候、对哪个键值、做了什么"。我习惯用两种方式:一种是用户态的API监控,另一种是内核态的回调监控。

用户态监控:简单直接

RegNotifyChangeKeyValue这个API就能监控某个注册表键的变化。但有个坑——它只能告诉你"变了",但变的是什么、谁变的,一概不知。

我在项目中遇到过这种情况:想监控某个关键键值是否被篡改,结果用这个API只能收到通知,还得自己再去读一遍值做对比。麻烦是麻烦了点,但胜在简单。

// 监控注册表键值变化的示例
HKEY hKey;
LONG lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
    TEXT("SOFTWARE\\MyApp\\Settings"),
    0, KEY_NOTIFY, &hKey);

if (lRet == ERROR_SUCCESS) {
    // 等待注册表变化通知
    lRet = RegNotifyChangeKeyValue(hKey,
        FALSE,           // 不监控子键
        REG_NOTIFY_CHANGE_LAST_SET,  // 监控值变化
        NULL,            // 不用事件对象
        FALSE);          // 异步模式
    // 收到通知后,重新读取键值做对比
}

内核态监控:更底层更全面

如果你真想搞清楚注册表的一举一动,得用内核态的CmRegisterCallback。这个回调函数能捕获几乎所有注册表操作——创建、打开、删除、修改、枚举,一个不落。

为什么会推荐用内核态?因为恶意软件也学精了,很多会绕过用户态API直接发IRP。你想想看,用户态监控根本抓不到它们。

核心要点:用户态监控适合轻量级场景,内核态回调才是真正的"全量监控"。但内核态开发风险高,一个蓝屏就能让你怀疑人生。

注册表API Hook:拦截并篡改

监控只是第一步。有时候我们需要拦截注册表操作,甚至篡改返回结果。这就是API Hook的用武之地。

Hook RegOpenKeyEx:偷梁换柱

我记得有一次分析一个勒索软件,它不断查询注册表判断是否在沙箱中运行。我直接Hook了RegOpenKeyEx,让它打开任何键都返回"沙箱特征不存在"——嗯,这招叫"以彼之道还施彼身"。

// Hook RegOpenKeyEx 的简化逻辑
LSTATUS WINAPI Hooked_RegOpenKeyEx(
    HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions,
    REGSAM samDesired, PHKEY phkResult)
{
    // 检查是否在访问我们关心的键
    if (wcsstr(lpSubKey, L"SandboxDetect") != NULL) {
        // 返回"找不到"来欺骗调用者
        return ERROR_FILE_NOT_FOUND;
    }
    // 其他情况正常放行
    return Real_RegOpenKeyEx(hKey, lpSubKey,
        ulOptions, samDesired, phkResult);
}

个人经验:Hook注册表API时,一定要处理好线程同步。我吃过一次亏——多线程同时调用Hook函数,结果死锁了,整个进程卡死。后来加了读写锁才搞定。

Hook RegQueryValueEx:篡改返回值

这个更常用。很多软件通过注册表读取配置,我们可以在中间改掉返回值。比如让某个软件以为自己是试用版,或者反过来让它以为自己是正版。

我曾经帮一个朋友调试他的软件——注册表里的授权信息总被篡改。后来发现是另一个程序Hook了RegQueryValueEx,在返回前偷偷改了数据。嗯,逆向的世界就是这么"套娃"。

注册表保护:守住最后一道防线

监控和Hook都是"主动出击",但有时候我们需要"被动防御"——保护注册表不被恶意修改。

ACL权限控制:最基础的保护

给注册表键设置ACL(访问控制列表),限制谁可以读写。这个方法简单有效,但有个问题——恶意软件如果有管理员权限,可以直接修改ACL。

// 设置注册表键的ACL,只允许SYSTEM访问
PSECURITY_DESCRIPTOR pSD = NULL;
PACL pACL = NULL;

// 创建只允许SYSTEM的ACL
// ... 省略具体代码,核心是调用SetEntriesInAcl

// 设置到注册表键
lRet = RegSetKeySecurity(hKey,
    DACL_SECURITY_INFORMATION, pSD);

内核回调保护:更硬核的方案

CmRegisterCallback不仅能监控,还能阻止操作。在回调函数中,如果发现是非法进程在修改关键键值,直接返回STATUS_ACCESS_DENIED

我建议把保护逻辑做成白名单模式——只有信任的进程才能修改,其他一律拦截。这样最安全,但维护起来也最麻烦。

注意:内核回调中做决策一定要快。回调函数里不能做耗时操作,否则系统性能会严重下降。我曾经见过一个驱动,在回调里查数据库,结果整个系统卡成PPT。

知识体系总览

下面这张图把注册表逆向的三个核心方向串起来了。你可以看到,监控、Hook、保护三者其实是递进关系——先知道谁在操作,再决定是否拦截,最后考虑如何防护。

注册表逆向三大核心方向 注册表操作监控 注册表API Hook 注册表保护 用户态API监控 内核态回调监控 RegOpenKeyEx 打开拦截 RegQueryValueEx 读取篡改 ACL权限控制 基础防护 内核回调保护 硬核拦截 实战建议 1. 先用用户态监控快速定位问题 2. 需要拦截时再上API Hook 3. 关键系统键值必须做内核级保护 4. 三者结合使用效果最佳

实战中的避坑指南

做注册表逆向这些年,踩过的坑不少。挑几个典型的说说:

  • 32位和64位的注册表重定向——64位系统上,32位程序访问注册表会被重定向到Wow6432Node。我刚开始不知道这个,调试了半天发现"明明写了值,怎么读不到"。
  • 注册表虚拟化——Vista之后,非管理员写HKLM会被虚拟化到HKCU。这个坑我踩过两次才记住。
  • 事务性注册表操作——RegCreateKeyTransacted这类API,普通监控根本抓不到。我曾经以为某个恶意软件没写注册表,结果人家用的是事务操作。

我的习惯:做注册表逆向时,先用Process Monitor抓一遍全量日志,看看目标程序到底访问了哪些键。然后再针对性地Hook或保护。这样效率最高,不会在无关的键上浪费时间。

注册表逆向这块,说白了就是"攻防对抗"的缩影。监控的人想看清一切,Hook的人想篡改一切,保护的人想阻止一切。而我们要做的,就是根据实际场景,灵活运用这三种技术。

嗯,这一章就到这里。记住一点:注册表是Windows的命门,动它之前,先想清楚后果。


公众号:蓝海资料掘金营,微信deep3321