第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、保护三者其实是递进关系——先知道谁在操作,再决定是否拦截,最后考虑如何防护。
实战中的避坑指南
做注册表逆向这些年,踩过的坑不少。挑几个典型的说说:
- 32位和64位的注册表重定向——64位系统上,32位程序访问注册表会被重定向到
Wow6432Node。我刚开始不知道这个,调试了半天发现"明明写了值,怎么读不到"。 - 注册表虚拟化——Vista之后,非管理员写
HKLM会被虚拟化到HKCU。这个坑我踩过两次才记住。 - 事务性注册表操作——
RegCreateKeyTransacted这类API,普通监控根本抓不到。我曾经以为某个恶意软件没写注册表,结果人家用的是事务操作。
我的习惯:做注册表逆向时,先用Process Monitor抓一遍全量日志,看看目标程序到底访问了哪些键。然后再针对性地Hook或保护。这样效率最高,不会在无关的键上浪费时间。
注册表逆向这块,说白了就是"攻防对抗"的缩影。监控的人想看清一切,Hook的人想篡改一切,保护的人想阻止一切。而我们要做的,就是根据实际场景,灵活运用这三种技术。
嗯,这一章就到这里。记住一点:注册表是Windows的命门,动它之前,先想清楚后果。
公众号:蓝海资料掘金营,微信deep3321