x86下可以使用hook技术,x64下使用回调监控。
主要函数CmRegisterCallback(RegistryCallback, NULL, &CmHandle);
原型
NTSTATUS CmRegisterCallback(
PEX_CALLBACK_FUNCTION Function,
PVOID Context,
PLARGE_INTEGER Cookie
);
第一个参数就是我们的回调函数,自己想实现的逻辑就是在这里写,中间这个微软的解释是A driver-defined value that the configuration manager will pass as the CallbackContext parameter to the RegistryCallback routine,配置管理器将作为CallbackContext参数传递给RegistryCallback例程的驱动程序定义的值,我目前没用到过,最后一个是指向LARGE_INTEGER变量的指针,该变量接收标识回调例程的值。取消注册回调例程时,将此值作为Cookie参数传递给CmUnRegisterCallback。关闭注册表回调会用到。
第一个参数也就是我们实现的函数申明如下
NTSTATUS RegistryCallback
(
IN PVOID CallbackContext,
IN PVOID Argument1,//操作类型,
IN PVOID Argument2//操作的结构体指针
)
也就是第二个参数是操作注册表的类型,然后操作内容等可以通过第三个参数拿到,下面是我进行操作的监控代码demo片段
NTSTATUS RegistryCallback
(
IN PVOID CallbackContext,
IN PVOID Argument1,//操作类型,
IN PVOID Argument2//操作的结构体指针
)
{
if (!isOpenReg)
{
return STATUS_SUCCESS;
}
ULONG Options = 0;//记录操作类型 4创建,5删除,6设置键值,7删除键值,8重命名键
BOOLEAN isAllow = TRUE;//是否放行
PFQDRV_NOTIFICATION notification = NULL;
ULONG replyLength = 0;
BOOLEAN SafeToOpen;
SafeToOpen = TRUE;
NTSTATUS status = STATUS_SUCCESS;
long type;
NTSTATUS CallbackStatus = STATUS_SUCCESS;
UNICODE_STRING registryPath;
registryPath.Length = 0;
registryPath.MaximumLength = 2048 * sizeof(WCHAR);
registryPath.Buffer = ExAllocatePoolWithTag(NonPagedPool, registryPath.MaximumLength, REGISTRY_POOL_TAG);
notification = ExAllocatePoolWithTag(NonPagedPool,
sizeof(FQDRV_NOTIFICATION),
'nacS');
if (NULL == notification)
{
CallbackStatus = STATUS_INSUFFICIENT_RESOURCES;
return CallbackStatus;
}
if (registryPath.Buffer == NULL)
return STATUS_SUCCESS;
type = (REG_NOTIFY_CLASS)Argument1;
switch (type)
{
case RegNtPreCreateKeyEx: //出现两次是因为一次是OpenKey,一次是createKey
{
if (IsProcessName("regedit.exe", PsGetCurrentProcess()))
{
GetRegistryObjectCompleteName(®istryPath, NULL, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);
DbgPrint("[RegNtPreCreateKeyEx]KeyPath: %wZ", ®istryPath); //新键的路径
DbgPrint("[RegNtPreCreateKeyEx]KeyName: %wZ",
((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);//新键的名称
notification->Operation = 4;
char newPath[MAX_PATH] = { 0 };
WCHAR newPath1[MAX_PATH] = { 0 };
UnicodeToChar(®istryPath, newPath);
CharToWchar(newPath, newPath1);
wcscpy_s(notification->ProcessPath, MAX_PATH, newPath1);
char newName[MAX_PATH] = { 0 };
WCHAR newName1[MAX_PATH] = { 0 };
UnicodeToChar(((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName, newName);
CharToWchar(newName, newName1);
wcscpy_s(notification->TargetPath, MAX_PATH, newName1);
replyLength = sizeof(FQDRV_REPLY);
status = FltSendMessage(FQDRVData.Filter,
&FQDRVData.ClientPort,
notification,
sizeof(FQDRV_NOTIFICATION),
notification,
&replyLength,
NULL);
if (STATUS_SUCCESS == status) {
SafeToOpen = ((PFQDRV_REPLY)notification)->SafeToOpen;
if (SafeToOpen)
{
CallbackStatus = STATUS_SUCCESS;
}
else {
CallbackStatus = STATUS_ACCESS_DENIED;
}
}
//CallbackStatus = STATUS_ACCESS_DENIED;
}
break;
}
case RegNtPreDeleteKey:
{
if (IsProcessName("regedit.exe", PsGetCurrentProcess()))
{
GetRegistryObjectCompleteName(®istryPath, NULL, ((PREG_DELETE_KEY_INFORMATION)Argument2)->Object);
DbgPrint("[RegNtPreDeleteKey]%wZ", ®istryPath); //新键的路径
notification->Operation = 5;
char newPath[MAX_PATH] = { 0 };
WCHAR newPath1[MAX_PATH] = { 0 };
UnicodeToChar(®istryPath, newPath);
CharToWchar(newPath, newPath1);
wcscpy_s(notification->ProcessPath, MAX_PATH, newPath1);
replyLength = sizeof(FQDRV_REPLY);
status = FltSendMessage(FQDRVData.Filter,
&FQDRVData.ClientPort,
notification,
sizeof(FQDRV_NOTIFICATION),
notification,
&replyLength,
NULL);
if (STATUS_SUCCESS == status) {
SafeToOpen = ((PFQDRV_REPLY)notification)->SafeToOpen;
if (SafeToOpen)
{
CallbackStatus = STATUS_SUCCESS;
}
else {
CallbackStatus = STATUS_ACCESS_DENIED;
}
}
//CallbackStatus = STATUS_ACCESS_DENIED;
}
break;
}
case RegNtPreSetValueKey:
{
if (IsProcessName("regedit.exe", PsGetCurrentProcess()))
{
GetRegistryObjectCompleteName(®istryPath, NULL, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object);
DbgPrint("[RegNtPreSetValueKey]KeyPath: %wZ", ®istryPath);
DbgPrint("[RegNtPreSetValueKey]ValName: %wZ", ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName);
notification->Operation = 6;
char newPath[MAX_PATH] = { 0 };
WCHAR newPath1[MAX_PATH] = { 0 };
UnicodeToChar(®istryPath, newPath);
CharToWchar(newPath, newPath1);
wcscpy_s(notification->ProcessPath, MAX_PATH, newPath1);
char newName[MAX_PATH] = { 0 };
WCHAR newName1[MAX_PATH] = { 0 };
UnicodeToChar(((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName, newName);
CharToWchar(newName, newName1);
wcscpy_s(notification->TargetPath, MAX_PATH, newName1);
replyLength = sizeof(FQDRV_REPLY);
status = FltSendMessage(FQDRVData.Filter,
&FQDRVData.ClientPort,
notification,
sizeof(FQDRV_NOTIFICATION),
notification,
&replyLength,
NULL);
if (STATUS_SUCCESS == status) {
SafeToOpen = ((PFQDRV_REPLY)notification)->SafeToOpen;
if (SafeToOpen)
{
CallbackStatus = STATUS_SUCCESS;
}
else {
CallbackStatus = STATUS_ACCESS_DENIED;
}
}
//CallbackStatus = STATUS_ACCESS_DENIED;
}
break;
}
case RegNtPreDeleteValueKey:
{
if (IsProcessName("regedit.exe", PsGetCurrentProcess()))
{
GetRegistryObjectCompleteName(®istryPath, NULL, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object);
DbgPrint("[RegNtPreDeleteValueKey]KeyPath: %wZ", ®istryPath);
DbgPrint("[RegNtPreDeleteValueKey]ValName: %wZ", ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName);
notification->Operation = 7;
char newPath[MAX_PATH] = { 0 };
WCHAR newPath1[MAX_PATH] = { 0 };
UnicodeToChar(®istryPath, newPath);
CharToWchar(newPath, newPath1);
wcscpy_s(notification->ProcessPath, MAX_PATH, newPath1);
char newName[MAX_PATH] = { 0 };
WCHAR newName1[MAX_PATH] = { 0 };
UnicodeToChar(((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName, newName);
CharToWchar(newName, newName1);
wcscpy_s(notification->TargetPath, MAX_PATH, newName1);
replyLength = sizeof(FQDRV_REPLY);
status = FltSendMessage(FQDRVData.Filter,
&FQDRVData.ClientPort,
notification,
sizeof(FQDRV_NOTIFICATION),
notification,
&replyLength,
NULL);
if (STATUS_SUCCESS == status) {
SafeToOpen = ((PFQDRV_REPLY)notification)->SafeToOpen;
if (SafeToOpen)
{
CallbackStatus = STATUS_SUCCESS;
}
else {
CallbackStatus = STATUS_ACCESS_DENIED;
}
}
//CallbackStatus = STATUS_ACCESS_DENIED;
}
break;
}
case RegNtPreRenameKey:
{
if (IsProcessName("regedit.exe", PsGetCurrentProcess()))
{
GetRegistryObjectCompleteName(®istryPath, NULL, ((PREG_RENAME_KEY_INFORMATION)Argument2)->Object);
DbgPrint("[RegNtPreRenameKey]KeyPath: %wZ", ®istryPath);
DbgPrint("[RegNtPreRenameKey]NewName: %wZ", ((PREG_RENAME_KEY_INFORMATION)Argument2)->NewName);
notification->Operation = 8;
char newPath[MAX_PATH] = { 0 };
WCHAR newPath1[MAX_PATH] = { 0 };
UnicodeToChar(®istryPath, newPath);
CharToWchar(newPath, newPath1);
wcscpy_s(notification->ProcessPath, MAX_PATH, newPath1);
char newName[MAX_PATH] = { 0 };
WCHAR newName1[MAX_PATH] = { 0 };
UnicodeToChar(((PREG_RENAME_KEY_INFORMATION)Argument2)->NewName, newName);
CharToWchar(newName, newName1);
wcscpy_s(notification->TargetPath, MAX_PATH, newName1);
replyLength = sizeof(FQDRV_REPLY);
status = FltSendMessage(FQDRVData.Filter,
&FQDRVData.ClientPort,
notification,
sizeof(FQDRV_NOTIFICATION),
notification,
&replyLength,
NULL);
if (STATUS_SUCCESS == status) {
SafeToOpen = ((PFQDRV_REPLY)notification)->SafeToOpen;
if (SafeToOpen)
{
CallbackStatus = STATUS_SUCCESS;
}
else {
CallbackStatus = STATUS_ACCESS_DENIED;
}
}
//CallbackStatus = STATUS_ACCESS_DENIED;
}
break;
}
//『注册表编辑器』里的“重命名键值”是没有直接函数的,是先SetValueKey再DeleteValueKey
default:
break;
}
if (registryPath.Buffer != NULL)
ExFreePoolWithTag(registryPath.Buffer, REGISTRY_POOL_TAG);
return CallbackStatus;
}