///
//
// RegMon学习与分析
//
//
//
//
// 1.注册表回调机制
//
/
应用程序通过调用NtNotifyChangeKey或NtNotifyChangeMuptipleKeys系统服务,
可以监视一个或多个注册表创建,删除和修改动作.(ring3 api 对应的是RegNotifyChangeKey)
类似的进程和线程监视的话,可以使用PsSetCreateProcessNotifyRoutine()和
PsSetCreateThreadNotifyRoutine(),这两个函数可以通过向系统注册一个
CALLBALCK 函数来监视进程/线程等操作.
(具体实现可以参考sinister的<<编写进程\线程监视器>>)
注册表的回调机制是在Windows xp中引入的.但是RegMon在Windows xp上运行时,它使用的是SSDT hook.
因为Windows xp的回调机制并没有报告所有的注册表活动.
///
//
// 2.RegMon驱动分析
//
///
RegMon驱动主要是通过SSDT hook,hook了那些与注册表操作相关的系统服务函数.
Hook了以下函数:
@ NtCreateKey()
@ NtDeleteKey()
@ NtEnumerateKey()
@ NtEnumerateValueKey()
@ NtFlushKey()
@ NtLoadKey()
@ NtOpenKey()
@ NtQueryKey()
@ NtQueryValueKey()
@ NtUnloadKey()
@ NtSetValueKey()
@ NtCloseKey()
要实现注册表的监控就必须hook掉这些函数.(当然也可以hook更底层的,比如Cmxxxx系列的.)
RegMon内部机理如下:
// 1. 应用程序执行与注册表有关的系统服务调用,就会调用上述那些与注册表相关的函数.
// 2. 而这些函数已经被Regmon.sys hook掉了,那么应用程序对注册表的操作,就能被RegMon捕获.
//
//
// 下面的分析有点混乱,权当笔记看..
//
/
//---------------------------------
// 1.DriverEntry()
//---------------------------------
// segment 1
// RTL_QUERY_REGISTRY_TABLE 结构定义如下:
typedef struct _RTL_QUERY_REGISTRY_TABLE {
PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine;
ULONG Flags;
PWSTR Name;
PVOID EntryContext;
ULONG DefaultType;
PVOID DefaultData;
ULONG DefaultLength;
} RTL_QUERY_REGISTRY_TABLE, *PRTL_QUERY_REGISTRY_TABLE;
RTL_QUERY_REGISTRY_TABLE paramTable[2];
RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE,
registryPath.Buffer, ¶mTable[0],
NULL, NULL );
// RTL_QUERY_REGISTRY_TABLE paramTable[2];
// 是一个RTL_QUERY_REGISTRY_TABLE结构的数组。
// 每个成员都包含一个Name名称项和一些其他值。
// 因为要求这种数组必须以一个“空数组”结束。
// 所以空数组就是说Name为空和QueryRoutine为空。
// The RtlQueryRegistryValues()allows the caller to
// query several values from the registry subtree with a single call.
// segment 2
1. IoCreateDevice() 创建device
2. IoCreateSymbolicLink() 创建符号链接symbolicLink
3. 设置分发函数RegmonDispatch;
4. DriverObject->DriverUnload = RegmonUnload;
5. 初始化几个KMutex,和rootkey lengths
6. 获得一个指向SSDT表的指针KeServiceTablePointers
关于RegmonMapServiceTable()这个函数,后续奉上.
7. 获得"System"进程的NameOffset..GetProcessNameOffset(),暂时不知道用来干嘛..
8. FullPathLookaside指向一个Loskaside内存..估计是用来保存注册表路径的~
9. RegmonUpdageFilters();---后面分析
10.HookRegistry(),hook上述与注册表相关的系统服务函数.
//----------------------------------------------
// 2. HookRegistry()和UnHookRegistry()函数分析
//------------------------------------------------
"HookRegistry()的功能是实现函数的hook,主要是通过"
"SYSCALL_INDEX()宏和HOOK_SYSCALL()宏实现的"
"UnHookRegistry()是实现函数钩子的卸载,即unhook,通过
"SYSCALL_INDEX()宏和UNHOOK_SYSCALL()宏实现的"
SYSCALL_INDEX宏采用Zw*函数地址并返回它在SSDT中相应的索引号.
#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
"实现原理:内核中所有Zw*函数都以操作码 mov eax,ULONG,起始其中ULONG是
"系统调用在SSDT的索引号.通过将该函数的第二字节看作ULONG类型,这些宏能够得到该
"函数的索引号."
HOOK_SYSCALL和UNHOOK_SYSCALL采用被钩住的Zw*函数的地址,获取其索引号,并自动将
SSDT中该索引的相应地址与_HOOK函数地址进行交换.
/
//
// HookRegXXXX()函数的实现
//
//---------------------------------------------------
// 1. NtOpenKey() 和 HookRegOpenKey()
//---------------------------------------------------
NTSTATUS
ZwOpenKey(
OUT PHANDLE KeyHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
);
NtOpenKey()实现的是打开一个存在的Registry Key,并返回它的一个句柄hKey;
由于Regmon实现的功能是获得操作注册表应用程序的相关信息.
故必须从ObjectAttributes中获得..所以HookRegOpenKey()实现的功能
也就是过滤ObjectAttributes,从中获得相关信息,传送到内存缓冲区,最后传递给
RegMon GUI程序.显示出来. 然后也会调用原始的NtOpenKey()函数.
NTSTATUS
NTAPI
HookRegOpenKey(
IN OUT PHANDLE pHandle,
IN ACCESS_MASK ReqAccess,
IN POBJECT_ATTRIBUTES pOpenInfo
)
// GetFullName()获得一个key值的全路径,返回在fullname中.
GetFullName( pOpenInfo->RootDirectory, pOpenInfo->ObjectName, fullname );
// 调用原始的NtOpenKey()函数;
ntstatus = RealRegOpenKey( pHandle, ReqAccess, pOpenInfo );
//后续有一些关于hash table 的,暂时放下~
@ RegmonFreeHashEntry() // 从 hash table 移除
@ RegmonLogHash() // 加进hash table 中
@ LogRecord() // 输出类似信息
//"System:4 OpenKey HKLM\Software\Primax\Mouse Suite 98\Productivity Utility
// NOTFOUND"
//---------------------------------------------------
// 2. NtCreateKey() 和 HookRegCreateKey()
//---------------------------------------------------
实现方式和上面介绍的HookRegOpenKey()是一致的.这里就不赘述了.
//---------------------------------------------------
// 3. NtCloseKey() 和 HookRegCloseKey()
//---------------------------------------------------
由于NtCloseKey()值接受一个参数hKey,所以在
HookRegCloseKey()中需要使用原始的NtQueryKey()函数.
来获取更多关于该hKey的信息.传入给NtQueryKey()的是KEY_BASIC_INFORMATION
//--------------------------------------------------
// 4. HookRegFlushKey()
// 5. HookRegDeleteKey()
// 6. HookRegDeleteValueKey()
// 7. HookRegSetValueKey()
// 8. HookRegEnumerateKey()
// 9. HookRegQueryKey()
// 10. HookRegSetValueKey()
// .......
//--------------------------------------------------
这些函数实现方式与上面介绍的,大同小异,基本一致.
需要注意的几个函数是:
@ AppendRegValueData() // 获取更多关于RegValue的信息.
@ AppendKeyInformation() // 获取更多关于Key的信息
//-------------------------------------------------
//