00000030.ReverseAnalysis.ring0层注册表监控
links
深入理解注册表监控 从0环监测注册表机制...好像还是有点门槛的
如何监控
注册表是windows的重要数据库,存放了很多重要的信息以及一些应用的设置,对注册表进行监控并防止篡改是十分有必要的。
在64位系统下微软提供了CmRegisterCallback
这个回调函数来实时监控注册表的操作,
那么既然这里微软提供了这么一个方便的接口,
病毒木马自然也会利用,这里我们就来探究其实现的原理和如何利用这个回调函数进行对抗
CmRegisterCallback
NTSTATUS CmRegisterCallback(
[in] PEX_CALLBACK_FUNCTION Function,//回调函数
[in, optional] PVOID Context,
[out] PLARGE_INTEGER Cookie
);
参数1: 一个回调函数,
参数2: 配置管理器,将作为CallbackContext参数传递给RegistryCallback例程的驱动程序定义的值,所以
参数2为参数1服务
参数3: 指向 LARGE_INTEGER 变量的指针,该变量接收标识回调例程的值。当您取消注册回调例程时,请将此值作为Cookie参数传递给CmUnRegisterCallback
回调函数类型如下
EX_CALLBACK_FUNCTION ExCallbackFunction;
NTSTATUS ExCallbackFunction(
[in] PVOID CallbackContext,
[in, optional] PVOID Argument1,
[in, optional] PVOID Argument2
)
{
}
参数1: 被CmRegisterCallback传递过来的东西
参数2: 一个enum类型,REG_NOTIFY_CLASS, 用于标识正在执行的注册表操作的类型以及注册表操作执行之前还是之后调用RegistryCallback
参数3: 一个结构体,结构体的类型取决于参数2, 指向包含特定于注册表操作类型的信息的结构的指针
Argument1对应的枚举值如下
有些Argument1他是一样的,比如enum的RegNtPreDeleteKey和RegNtDeleteKey是一样的
为什么是一样的?因为他们对应的结构体是一样的,所以为了方便,枚举值也是一样的
typedef enum _REG_NOTIFY_CLASS {
RegNtDeleteKey,
RegNtPreDeleteKey = RegNtDeleteKey,
RegNtSetValueKey,
RegNtPreSetValueKey = RegNtSetValueKey,
RegNtDeleteValueKey,
RegNtPreDeleteValueKey = RegNtDeleteValueKey,
RegNtSetInformationKey,
RegNtPreSetInformationKey = RegNtSetInformationKey,
RegNtRenameKey,
RegNtPreRenameKey = RegNtRenameKey,
RegNtEnumerateKey,
RegNtPreEnumerateKey = RegNtEnumerateKey,
RegNtEnumerateValueKey,
RegNtPreEnumerateValueKey = RegNtEnumerateValueKey,
RegNtQueryKey,
RegNtPreQueryKey = RegNtQueryKey,
RegNtQueryValueKey,
RegNtPreQueryValueKey = RegNtQueryValueKey,
RegNtQueryMultipleValueKey,
RegNtPreQueryMultipleValueKey = RegNtQueryMultipleValueKey,
RegNtPreCreateKey,
RegNtPostCreateKey,
RegNtPreOpenKey,
RegNtPostOpenKey,
RegNtKeyHandleClose,
RegNtPreKeyHandleClose = RegNtKeyHandleClose,
//
// .Net only
//
RegNtPostDeleteKey,
RegNtPostSetValueKey,
RegNtPostDeleteValueKey,
RegNtPostSetInformationKey,
RegNtPostRenameKey,
RegNtPostEnumerateKey,
RegNtPostEnumerateValueKey,
RegNtPostQueryKey,
RegNtPostQueryValueKey,
RegNtPostQueryMultipleValueKey,
RegNtPostKeyHandleClose,
RegNtPreCreateKeyEx,
RegNtPostCreateKeyEx,
RegNtPreOpenKeyEx,
RegNtPostOpenKeyEx,
//
//
RegNtPreFlushKey,
RegNtPostFlushKey,
RegNtPreLoadKey,
RegNtPostLoadKey,
RegNtPreUnLoadKey,
RegNtPostUnLoadKey,
RegNtPreQueryKeySecurity,
RegNtPostQueryKeySecurity,
RegNtPreSetKeySecurity,
RegNtPostSetKeySecurity,
//
// per-object context cleanup
//
RegNtCallbackObjectContextCleanup,
//
// new in Vista SP2
//
RegNtPreRestoreKey,
RegNtPostRestoreKey,
RegNtPreSaveKey,
RegNtPostSaveKey,
RegNtPreReplaceKey,
RegNtPostReplaceKey,
MaxRegNtNotifyClass //should always be the last enum
} REG_NOTIFY_CLASS;
这里有几个比较常见的类型
- RegNtPreCreateKey 创建注册表 对应的
Argument2
为PREG_CREATE_KEY_INFORMATION
- RegNtPreOpenKey 打开注册表 对应的
Argument2
为PREG_CREATE_KEY_INFORMATION
- RegNtPreDeleteKey 删除键 对应的
Argument2
为PREG_DELETE_KEY_INFORMATION
- RegNtPreDeleteValueKey 删除键值 对应的
Argument2
为PREG_DELETE_VALUE_KEY_INFORMATION
- RegNtPreSetValueKey 修改键值 对应的
Argument2
为PREG_SET_VALUE_KEY_INFORMATION
RegNtPreCreateKey
和RegNtPreOpenKey
的Argument2
都是使用的PREG_CREATE_KEY_INFORMATION
这个结构体,
我们看下结构
typedef struct _REG_CREATE_KEY_INFORMATION {
PUNICODE_STRING CompleteName; //指向路径的指针
PVOID RootObject; //指向注册表项的指针
PVOID ObjectType;
ULONG CreateOptions;
PUNICODE_STRING Class;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
ACCESS_MASK DesiredAccess;
ACCESS_MASK GrantedAccess;
PULONG Disposition;
PVOID *ResultObject;
PVOID CallContext;
PVOID RootObjectContext;
PVOID Transaction;
PVOID Reserved;
}
REG_CREATE_KEY_INFORMATION,
REG_OPEN_KEY_INFORMATION,
*PREG_CREATE_KEY_INFORMATION,
*PREG_OPEN_KEY_INFORMATION;
再来看看另外一个结构体, RegNtPreDeleteKey
对应PREG_DELETE_KEY_INFORMATION
结构体
typedef struct _REG_DELETE_KEY_INFORMATION {
PVOID Object; //指向要删除注册表的指针
PVOID CallContext;
PVOID ObjectContext;
PVOID Reserved;
} REG_DELETE_KEY_INFORMATION, *PREG_DELETE_KEY_INFORMATION, REG_FLUSH_KEY_INFORMATION, *PREG_FLUSH_KEY_INFORMATION;
然后是RegNtPreDeleteValueKey
对应PREG_DELETE_VALUE_KEY_INFORMATION
结构体,
通过名字可以判断这里我们要删除表项里面的值
typedef struct _REG_DELETE_VALUE_KEY_INFORMATION {
PVOID Object; //指向要删除的注册表的指针
PUNICODE_STRING ValueName; //指向具体需要删除的值
PVOID CallContext;
PVOID ObjectContext;
PVOID Reserved;
} REG_DELETE_VALUE_KEY_INFORMATION, *PREG_DELETE_VALUE_KEY_INFORMATION;
RegNtPreSetValueKey
对应的是PREG_SET_VALUE_KEY_INFORMATION
结构体
同样是 Object
指向要修改的注册表的指针,而ValueName
就是指向具体需要修改的值
typedef struct _REG_SET_VALUE_KEY_INFORMATION {
PVOID Object; //指向要修改的注册表的指针
PUNICODE_STRING ValueName; //就是指向具体需要修改的值
ULONG TitleIndex;
ULONG Type;
PVOID Data;
ULONG DataSize;
PVOID CallContext;
PVOID ObjectContext;
PVOID Reserved;
} REG_SET_VALUE_KEY_INFORMATION, *PREG_SET_VALUE_KEY_INFORMATION;
文章<<深入理解注册表监控 >>中,作者对 注册表监控函数 CmRegisterCallback
简单的逆向了一下
了解到CmRegisterCallback
其实就是申请了一块空间,保存了一个双向链表、Cookie、Context、回调函数的地址,
那么如果要存放注册表,只可能是放在了双向链表里面,
这里我们就有理由猜测注册表监控的回调函数就是通过一个双向链表连接起来的
在后面的反注册表监控中,,我们也会用到这里的结论
其实...感觉会用就行,,,目前还接触不到那么深的原理
根据作者的讲解 << 深入理解注册表监控 >> 我尝试去复刻他给出的代码....
代码见 https://github.com/redqx/malware_coding/tree/master/ring0-monitor-regedit
代码测试:
windbg输出
我们尝试对计算机\HKEY_CURRENT_USER\SOFTWARE\demo
的demo1项进行字符串值得修改
同时驱动文件监控得也是计算机\HKEY_CURRENT_USER\SOFTWARE\demo
加载驱动得时候,修改注册表失败
驱动卸载后,注册表修改无报错,修改值为happy
反监控
通过文章<< 深入理解注册表监控 >>了解到CmRegisterCallback
的底层实现,就是通过申请一块内存空间存放双向链表,
那么我们只要定位到这个双向链表删除回调函数即可得到反监控的效果
呃....<< 深入理解注册表监控 >>的作者是从驱动的方式去实现一个反监控,
也就是进入内核层,然后去删除一些信息,,实现反监控
这里的话,,,我就不再深入了解了,,,以后看情况再回来看看吧...
但也还是大概阐述一下,我看懂了什么吧,,,,,,
作者通过硬编码的方式去获取了在注册回调函数时的Cookie
然后调用CmUnRegisterCallback函数,参数是之前获取的Cookie
然后取消回调函数,,实现了一个unhook,从而逃避了注册表的监控
对此..我的比喻是,,,卸掉敌人的刀子,,实现自身的安全,,,,,
标签:00000030,ReverseAnalysis,INFORMATION,PREG,KEY,注册表,PVOID,REG From: https://www.cnblogs.com/redqx/p/17983350