来源于一个注册表保护工具的逆向,主要利用CmpParseKey函数的Object HOOK实现注册表操作重定向来实现注册表还原保护,驱动代码如下:

代码:
/*一个简单的注册表还原驱动
来源于某工具逆向,主要解决以下两个问题:
一、理解HIVE文件,这个被system进程独占
二、CmpParseKey函数的挂钩,object HOOK 

by:liuke_blue
E-mail:liuke_blue@126.com
*/

#include <ntddk.h>
#include <windef.h>
#include <stdio.h>

extern POBJECT_TYPE *IoFileObjectType; 

#define KeyValueFullInformation 1 
#define RegHiveFileName  L"\\WINDOWS\\system32\\config\\system"
#define RepointRegItem   L"\\REGISTRY\\USER\\.DEFAULT\\Volatile\\"   
#define ObjectTypeName   L"\\ObjectTypes\\Key"
#define  itemlen 0x0E


 
typedef enum _SYSTEM_INFORMATION_CLASS { 
SystemBasicInformation,      // 0 
   SystemProcessorInformation,     // 1 
   SystemPerformanceInformation,     // 2
   SystemTimeOfDayInformation,     // 3
   SystemNotImplemented1,      // 4
   SystemProcessesAndThreadsInformation,    // 5
   SystemCallCounts,       // 6
   SystemConfigurationInformation,     // 7
   SystemProcessorTimes,      // 8
   SystemGlobalFlag,       // 9
   SystemNotImplemented2,      // 10
   SystemModuleInformation,      // 11
   SystemLockInformation,      // 12
   SystemNotImplemented3,      // 13
   SystemNotImplemented4,      // 14
   SystemNotImplemented5,      // 15
   SystemHandleInformation,      // 16
   SystemObjectInformation,      // 17
   SystemPagefileInformation,      // 18
   SystemInstructionEmulationCounts,     // 19
   SystemInvalidInfoClass1,      // 20
   SystemCacheInformation,      // 21
   SystemPoolTagInformation,      // 22
   SystemProcessorStatistics,      // 23
   SystemDpcInformation,      // 24
   SystemNotImplemented6,      // 25
   SystemLoadImage,       // 26
   SystemUnloadImage,      // 27
   SystemTimeAdjustment,      // 28
   SystemNotImplemented7,      // 29
   SystemNotImplemented8,      // 30
   SystemNotImplemented9,      // 31
   SystemCrashDumpInformation,     // 32
   SystemExceptionInformation,     // 33
   SystemCrashDumpStateInformation,     // 34
   SystemKernelDebuggerInformation,     // 35
   SystemContextSwitchInformation,     // 36
   SystemRegistryQuotaInformation,     // 37
   SystemLoadAndCallImage,      // 38
   SystemPrioritySeparation,      // 39
   SystemNotImplemented10,      // 40
   SystemNotImplemented11,      // 41
   SystemInvalidInfoClass2,      // 42
   SystemInvalidInfoClass3,      // 43
   SystemTimeZoneInformation,      // 44
   SystemLookasideInformation,     // 45
   SystemSetTimeSlipEvent,      // 46
   SystemCreateSession,      // 47
   SystemDeleteSession,      // 48
   SystemInvalidInfoClass4,      // 49
   SystemRangeStartInformation,     // 50
   SystemVerifierInformation,      // 51
   SystemAddVerifier,      // 52
   SystemSessionProcessesInformation     // 53
} SYSTEM_INFORMATION_CLASS;



typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG            ProcessId;
    UCHAR            ObjectTypeNumber;
    UCHAR            Flags;
    USHORT           Handle;
    PVOID            Object;
    ACCESS_MASK      GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;



NTSYSAPI
NTSTATUS 
NTAPI
NtQuerySystemInformation(IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
                         OUT PVOID SystemInformation,
                         IN ULONG SystemInformationLength,
                         OUT PULONG ReturnLength OPTIONAL);

NTSYSAPI
NTSTATUS 
NTAPI ObOpenObjectByPointer(IN PVOID Object,
                            IN ULONG HandleAttributes,
                            IN PACCESS_STATE PassedAccessState,
                            IN  ACCESS_MASK DesiredAccess,
                            IN  POBJECT_TYPE ObjectType,
                            IN  KPROCESSOR_MODE AccessMode,
                            OUT PHANDLE Handle);


NTSYSAPI
NTSTATUS
NTAPI
ZwRestoreKey(IN HANDLE KeyHandle,
             IN HANDLE FileHandle,
             IN ULONG  Flags);


NTSYSAPI 
NTSTATUS 
NTAPI 
NtClose(IN HANDLE ObjectHandle );


NTSYSAPI
NTSTATUS
NTAPI
ObOpenObjectByName(IN POBJECT_ATTRIBUTES ObjectAttributes,
                   IN POBJECT_TYPE ObjectType,
                   IN KPROCESSOR_MODE AccessMode,
                   IN PACCESS_STATE PassedAccessState,
                   IN ACCESS_MASK DesiredAccess,
                   IN OUT PVOID ParseContext,
                   OUT PHANDLE Handle);


NTSYSAPI
NTSTATUS
NTAPI
ObReferenceObjectByHandle(IN HANDLE Handle,
                          IN ACCESS_MASK DesiredAccess,
                          IN POBJECT_TYPE ObjectType,
                          IN KPROCESSOR_MODE AccessMode,
                          OUT PVOID* Object,
                          OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL);

//记得设置为全局变量
ULONG  PID; 
ULONG Orig_CmpParseKey;

NTSTATUS MyCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
ULONG InstallObjectTypeHook(BOOLEAN IsHook);
VOID RestoreReg(PVOID CurrentId);
NTSTATUS Restorssymbol();


//重定向指向打开项或者键值
NTSTATUS  fake_CmpParseKey(
                           IN PVOID ParseObject,
                           IN PVOID ObjectType,
                           IN OUT PACCESS_STATE AccessState,
                           IN KPROCESSOR_MODE AccessMode,
                           IN ULONG Attributes,
                           IN OUT PUNICODE_STRING CompleteName,
                           IN OUT PUNICODE_STRING RemainingName,
                           IN OUT PVOID Context OPTIONAL,
                           IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
                           OUT PVOID *Object)
{
   wchar_t ItemName[10];
   unsigned short  fullnamelen;
   WORD  name1Len;
   PVOID repoint_pool;
   ULONG name2Len;
   unsigned short poollen;
   unsigned int  childlen;  
   DWORD  tmppos;
   ULONG repoint_len;
   DWORD CompleteName_;
   NTSTATUS status;
   
   
   CompleteName_=CompleteName;
   fullnamelen=CompleteName->Length;
   name1Len=(WORD)(itemlen-2);

   if (fullnamelen<itemlen)
   { 
      if (fullnamelen==name1Len)  //不含"\\"的字符串长度
      {        
      //将L"SOFTWARE"拷贝至ItemName; 
      memcpy(ItemName,(void*)(CompleteName->Buffer),name1Len);
          //最后一位'\0'结尾
      ItemName[name1Len>>1]=0;
          //字符串转换为大写
      _wcsupr(ItemName);
          //如果匹配字符串"SOFTWARE"
      if (RtlCompareMemory(ItemName,L"SYSTEM\\",name1Len)==name1Len)
          {
             //分配重定向缓冲区
        repoint_pool=ExAllocatePool(PagedPool,0x40u);
        memcpy(repoint_pool,L"\\REGISTRY\\USER\\.DEFAULT\\Volatile\\",0x40u);     
              repoint_len=0x00420040;
        goto Over;
      }
    } 
   }
   else
   {
      memcpy(ItemName,(void*)(CompleteName->Buffer),itemlen);  
      ItemName[itemlen>>1]=0;
    _wcsupr(ItemName);
    name2Len=RtlCompareMemory(ItemName,L"SYSTEM\\",itemlen);
    fullnamelen=itemlen;
      if (name2Len==itemlen)
      {
         poollen=(WORD)CompleteName->Length-itemlen+0x42;
     repoint_pool=ExAllocatePool(PagedPool,poollen);
         memcpy(repoint_pool,L"\\REGISTRY\\USER\\.DEFAULT\\Volatile\\",0x42);     
         //后面子项的长度,除去前面"SOFTWARE\"的字符串剩下长度
     childlen=CompleteName->Length-(WORD)itemlen;
         tmppos=*(DWORD*)(CompleteName_+4);
     //把"SOFTWARE\\"后面所接的字符子串拷贝到重定向值后面
     memcpy((CHAR*)repoint_pool+0x42,(const void *)(tmppos+2*((WORD)(WORD)itemlen>>1)),childlen);      
       repoint_len=( ((DWORD)(poollen+2)<<16) | ((WORD)poollen) );   
Over:     
      ExFreePoolWithTag(*(PVOID*)(CompleteName_+4),0);
        *(DWORD*)CompleteName_=repoint_len;
        *(DWORD*)(CompleteName_+4)=(DWORD)repoint_pool;
      
    //重新解析
       return STATUS_REPARSE;
    }


  }

   __asm{
         push eax
       push Object
         push SecurityQos
     push Context
     push RemainingName
     push CompleteName_
         push Attributes
     push AccessMode
         push AccessState
         push ObjectType
         push ParseObject
     call Orig_CmpParseKey
         mov status,eax  
         pop eax
   }
 return status;
 
}




NTSTATUS  DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
   PDEVICE_OBJECT DeviceObject;
   UNICODE_STRING devicename;
   UNICODE_STRING linkname;
   NTSTATUS status;
   HANDLE Threadhandle=NULL;


   RtlInitUnicodeString(&devicename,L"\\Device\\RevertHive_2010");
   RtlInitUnicodeString(&linkname,L"\\DosDevices\\RevertHive_2010");
   
   status=IoCreateDevice(DriverObject,0,&devicename,FILE_DEVICE_UNKNOWN,0x600,FALSE,&DeviceObject);
   if (NT_SUCCESS(status))
   {
       status=IoCreateSymbolicLink(&linkname, &devicename);
     if (NT_SUCCESS(status))
     {
         
        DriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreateClose;
            DriverObject->MajorFunction[IRP_MJ_CLOSE]  = MyCreateClose;
        PID=PsGetCurrentProcessId(); 
      if (NT_SUCCESS(PsCreateSystemThread(&Threadhandle,0x1F03FFu, 0, 0, 0, RestoreReg, (PVOID)&PID)))
            ZwClose(Threadhandle); 
      InstallObjectTypeHook(1);         
      status=STATUS_SUCCESS;
     }else
     {
        IoDeleteDevice(DeviceObject);
     }
      
   }

return status;

}



ULONG InstallObjectTypeHook(BOOLEAN IsHook)
{
  OBJECT_ATTRIBUTES objAttr;
  UNICODE_STRING uObjName;
  NTSTATUS status;
  HANDLE KeyHandle;
  PVOID  keyObject;
  BYTE*  ParseProcedureOfObject;

  RtlInitUnicodeString(&uObjName,ObjectTypeName); 
  InitializeObjectAttributes(&objAttr,&uObjName,OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE ,NULL,NULL);
  
  status=ObOpenObjectByName(&objAttr,NULL,KernelMode,NULL,0,NULL,&KeyHandle);
  
  if (NT_SUCCESS(status))
  {
      status= ObReferenceObjectByHandle(KeyHandle,0x80000000u,NULL,KernelMode,(PVOID)&keyObject,0);
      if (NT_SUCCESS(status))
      {
        /*
          OBJECT_TYPE+0x60-->OBJECT_TYPE_INITIALIZER
          OBJECT_TYPE_INITIALIZER+0x3c-->ParseProcedure(nt!CmpParseKey)
          而且KeyObject指向OBJECT_TYPE,因此可以进行OBJECT HOOK
         */
      
    ParseProcedureOfObject=(BYTE*)keyObject+0x9C;
      if (!MmIsAddressValid((*(DWORD*)ParseProcedureOfObject)))
      {
        ObfDereferenceObject(keyObject);  
      ZwClose(KeyHandle);
      return status;
    }
    KdPrint(("CmpParseKey funcaddr=0x%X\n",*(DWORD*)ParseProcedureOfObject));
    //开始OBJECT HOOK
    if (IsHook)
    {
      __asm{
           push eax
                   push ecx
               mov  ecx,ParseProcedureOfObject
           mov  eax,offset fake_CmpParseKey
           xchg eax,[ecx]
             mov  Orig_CmpParseKey,eax
           pop ecx
           pop eax
      }
        
    
    }else
    {
      __asm{
                  push eax
                  push ecx
                  mov  eax,Orig_CmpParseKey                 
                  mov  ecx,ParseProcedureOfObject  
          xchg eax,[ecx] 
                  pop ecx
          pop eax
      }
      
    }
      
     ObfDereferenceObject(keyObject);
    }
    ZwClose(KeyHandle);
  }

return status;  
}


/*得到system进程里所独占的文件句柄(windows\system32\config\system),
然后重新装载在L"\\REGISTRY\\USER\\.DEFAULT\\Volatile\"下,实现注册表hive文件重定向
*/
VOID  RestoreReg(PVOID CurrentId)
{
  NTSTATUS status;
  PVOID pool;
  ULONG dwNeedsize;
  ULONG ReturnLength;
  unsigned int nCount;
  ULONG Object;
  ULONG PID;
  UNICODE_STRING *filetype;
  UNICODE_STRING szfile;
  PFILE_OBJECT FileObject=NULL;
  ULONG  current_handle_info_pos;
  UNICODE_STRING Hivename;
  HANDLE regedit_software_fileHandle;
  UNICODE_STRING volatile_reg;
  PUNICODE_STRING filename;
  OBJECT_ATTRIBUTES oa;
  ULONG numofhandles;


  RtlInitUnicodeString(&szfile,L"File");
  RtlInitUnicodeString(&Hivename,RegHiveFileName);
  dwNeedsize=0x1000;
  do 
  {
    pool=ExAllocatePool(PagedPool,dwNeedsize);
    if (pool)
  { 
      current_handle_info_pos=(ULONG)pool; 
    status= NtQuerySystemInformation(SystemHandleInformation, pool, dwNeedsize, &ReturnLength);  
  }else
    return NULL;
  
  if (status==STATUS_INFO_LENGTH_MISMATCH)
  {
      ExFreePoolWithTag(pool,0);
    dwNeedsize*=2;
  } 
  } while(status == STATUS_INFO_LENGTH_MISMATCH);
 /*+++
 这里注意pool指向的缓冲区的内容,查看文档如下:
 The data returned to the SystemInformation buffer is a ULONG count of the number of
 handles followed immediately by an array of SYSTEM_HANDLE_INFORMATION.很显然是指句柄的数量,
 紧接着就是SYSTEM_HANDLE_INFORMATION的数组,还是的看文档,才清楚.
  ++*/
  if (pool)
  {
    if (NT_SUCCESS(status))
     {
         numofhandles=*(ULONG*)current_handle_info_pos;
     nCount=0;
       Object=((ULONG)pool+0xC);
     while (nCount<numofhandles)
     {
       PID=*(DWORD*)(Object-0x8);  
        //如果是system进程,并且匹配文件,则打印
        if((PID==(*(DWORD*)CurrentId))&&(!RtlCompareUnicodeString(&szfile,(PUNICODE_STRING)(*(DWORD*)(*(DWORD*)Object-0x10)+64),TRUE)))
      {
                    filetype=(PUNICODE_STRING)(*(DWORD*)(*(DWORD*)Object-0x10)+64);
                   
           FileObject=*(PULONG)Object;  
          //KdPrint(("filename=%wZ,vpb=0x%X\n",(PUNICODE_STRING)(&FileObject->FileName),*(DWORD *)(FileObject + 8)));         
                 filename=(PUNICODE_STRING)(&FileObject->FileName);
                 if (filename)
           {   
             //找到HIVE文件:software
             if (!RtlCompareUnicodeString(filename,&Hivename,TRUE))
             {
                 KdPrint(("filetye=%wZ,Filename=%wZ,filelen=%d\n",filetype,filename,filename->Length));
               status=ObOpenObjectByPointer(FileObject,512,NULL,0,0,KernelMode,&regedit_software_fileHandle);   
                 if (NT_SUCCESS(status))
                 {
                     if (regedit_software_fileHandle)
                     {
                         
                  RtlInitUnicodeString(&volatile_reg,RepointRegItem);
                  InitializeObjectAttributes(&oa,&volatile_reg,OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,NULL,NULL);
                  //REG_OPTION_VOLATILE表示创建的项存放在在内存中
                                    status=ZwCreateKey((PHANDLE)(&CurrentId),0xF003Fu,&oa,0,0,5u,&current_handle_info_pos);
                                    if (NT_SUCCESS(status))
                                    {
                                      //把hive文件临时恢复在设定下的注册表项
                     status=ZwRestoreKey((HANDLE)CurrentId,regedit_software_fileHandle,8u);
                     if (NT_SUCCESS(status))
                                         Restorssymbol();   
                                      ZwClose((HANDLE)CurrentId);
                  }
                                     ZwClose(regedit_software_fileHandle);
                  goto while_break;
                 }
               
               }
             
             }
             
    
           }          
      } 
        ++nCount;
      Object+=0x10;
     }


 
   }
  
  
  
  }
while_break:
 ExFreePoolWithTag(pool,0);
return;

}



/*开起一个线程时刻监测是否被HOOK,防止被修复,猥琐的写法
VOID ProtectHook()
{
  ULONG Oldaddress; 
  LARGE_INTEGER Interval; 

  Interval.QuadPart=100*10000; 

  while (1)
  {
     Oldaddress=*(ULONG*)ParseKeyAddress;
   if (Oldaddress == Orig_CmpParseKey) 
     *(ULONG*)ParseKeyAddress=(ULONG)fake_CmpParseKey;
     
    KeDelayExecutionThread(0,0,&Interval);//延缓时间 
  }
    

}*/

NTSTATUS MyCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{

    Irp->IoStatus.Status = STATUS_SUCCESS;
  Irp->IoStatus.Information = 0;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);
    
  return Irp->IoStatus.Status;
}


//恢复符号连接
NTSTATUS Restorssymbol()
{
  UNICODE_STRING DestinationString;
  NTSTATUS status;
  OBJECT_ATTRIBUTES oa;
  HANDLE handle;
  HANDLE KeyHandle;
  UNICODE_STRING valuename; 
  PVOID KeyValueInformation;
  ULONG ResultLength;
  ULONG Disposition;
  UNICODE_STRING setvalue;
  CHAR unitevalue[50]={0}; 
  int controlvalue;
  ANSI_STRING ansistring;
  ANSI_STRING ansistring2;  
  UNICODE_STRING subkeyname;
  HANDLE  subkey;
  int currentconfigvalue;
  CHAR unitevalue2[160]={0}; 

  KeyValueInformation=ExAllocatePool(NonPagedPool,0x100);
  RtlInitUnicodeString(&DestinationString, L"\\Registry\\Machine\\System\\Select");
  InitializeObjectAttributes(&oa,&DestinationString,OBJ_CASE_INSENSITIVE,NULL,NULL);
  
  status= ZwOpenKey(&handle, 0x20019u, &oa);
  
  if (NT_SUCCESS(status))
  {
    RtlInitUnicodeString(&valuename, L"Current");

    status= ZwQueryValueKey(handle,&valuename,KeyValueFullInformation,KeyValueInformation,0x80,&ResultLength);  
    NtClose(handle);
    if (NT_SUCCESS(status))
    {
       //得到current的值
    controlvalue=*(int *)((char*)KeyValueInformation+0x24);
    RtlInitUnicodeString(&valuename,L"\\REGISTRY\\USER\\.DEFAULT\\Volatile\\CurrentControlSet"); 
     
    InitializeObjectAttributes(&oa,&valuename,OBJ_CASE_INSENSITIVE,NULL,NULL);  
    status= ZwCreateKey(&KeyHandle,KEY_CREATE_LINK,&oa, 0, 0,  REG_OPTION_VOLATILE |REG_OPTION_CREATE_LINK , &Disposition); 
      if (NT_SUCCESS(status))
      {
         RtlInitUnicodeString(&setvalue, L"SymbolicLinkValue");
       sprintf(&unitevalue,"\\REGISTRY\\USER\\.DEFAULT\\Volatile\\ControlSet%03d",controlvalue);
       RtlInitAnsiString(&ansistring,&unitevalue);  
       valuename.MaximumLength=256;         
       RtlAnsiStringToUnicodeString(&valuename,&ansistring, FALSE);
           status=ZwSetValueKey(KeyHandle, &setvalue, 0, REG_LINK, valuename.Buffer, valuename.Length);
       if (NT_SUCCESS(status))
       {
           RtlInitUnicodeString(&subkeyname, L"Control\\IDConfigDB");  
           InitializeObjectAttributes(&oa,&subkeyname,OBJ_CASE_INSENSITIVE,KeyHandle,NULL);
           
          status=ZwOpenKey(&subkey,0x20019u,&oa);
            NtClose(KeyHandle);
        if (NT_SUCCESS(status))
            {
                RtlInitUnicodeString(&valuename, L"CurrentConfig");
          status=ZwQueryValueKey(subkey,&valuename,KeyValueFullInformation,KeyValueInformation,0x80,&ResultLength);
          
            NtClose(subkey);
                    if (NT_SUCCESS(status))
                    {
                      currentconfigvalue= *(int *)((char *)KeyValueInformation + 0x30);  
            
            RtlInitUnicodeString(&subkeyname,L"\\REGISTRY\\USER\\.DEFAULT\\Volatile\\CurrentControlSet\\Hardware Profiles\\Current"); 
                       
                                                
            InitializeObjectAttributes(&oa,&subkeyname,OBJ_CASE_INSENSITIVE,KeyHandle,NULL);
            status= ZwCreateKey(&KeyHandle, KEY_CREATE_LINK, &oa, 0, 0, REG_OPTION_VOLATILE |REG_OPTION_CREATE_LINK, &Disposition);    
           if (NT_SUCCESS(status))
            {
                sprintf(&unitevalue2,"\\REGISTRY\\USER\\.DEFAULT\\Volatile\\CurrentControlSet\\Hardware Profiles\\%04d",currentconfigvalue);
                          RtlInitAnsiString(&ansistring2,&unitevalue2);
                      valuename.MaximumLength=256;
                          RtlAnsiStringToUnicodeString(&valuename,&ansistring2, FALSE); 
              ZwSetValueKey(KeyHandle,&setvalue,0,REG_LINK,valuename.Buffer,valuename.Length);
                          
              NtClose(KeyHandle);                  
          }
                     
        }
          
       }
           
         status= STATUS_SUCCESS;
    }
      else
       NtClose(KeyHandle);

  }
  
  }

  }

ExFreePool(KeyValueInformation);

return status;
  
}
ring3的代码我就不提供了,文章后提供该小工具的bin下载,本文已发表在黑客防线2011年第2期,
在此十分感谢sudami牛,目前对磁盘还原十分有兴趣,希望能够继续学习,谢谢!!
具体效果示意图如下:

上传的附件 KeyProtect.rar