转自:http://www.gomb.cn/?uid-2-action-viewspace-itemid-1457

10.1 注册表的结构
10.2 在驱动程序中访问注册表
10.3 RegistryWorks驱动程序源代码
   10.3.1 注册表键的创建与打开
   10.3.2 创建注册表键值
   10.3.3 访问注册表键值
   10.3.4 删除注册表键
   10.3.5 更新注册表键
 
源代码: KmdKit/examples/basic/RegistryWorks

10.1 注册表的结构

注册表(Registry)是基本数据的中心,在系统的设置和管理方面扮演着重要的角色。注册表的结构类似于磁盘的逻辑结构,但是注册表的内容不是磁盘数据的静态组合,而是随系统的工作进程而动态改变。注册表由keys构成,键就像磁盘的目录。最上层的keys叫做root keys。keys本身就是一个容器,装着其它的keys,装在里面的这些keys叫做subkeys或是values,就像磁盘上的文件。values保存着实际数据。对注册表的操作与管理是由Configuration Manager负责的。

root keys有六个:

HKEY_USER
 包含所有注册信息;
 
HKEY_CURRENT_USER
 保存着当前用户的注册信息;
 
HKEY_LOCAL_MACHINE
 保存着系统配置信息:硬件设备支持记录,安全策略,用户口令,应用程序设置和服务及驱动程序配置。
 
HKEY_CURRENT_CONFIG
 包含当前硬件配置信息;
 
HKEY_CLASSES_ROOT
 保存着文件关联和COM类的注册数据;

HKEY_PERFORMANCE_DATA
 包含着性能信息。
 
HKEY_PERFORMANCE_DATA 是一个特殊的键,未必会直接用到。在用户模式下,关于性能统计的信息是通过Performance Data Helper库来访问的,这个库实现在模块pdh.dll里。标准程序Performance Monitor正是利用了这个库。除性能统计之外这个键还包含着许多补充信息。例如,用于枚举进程、线程和模块等等的Process Status函数(在psapi.dll里实现)正是从HKEY_PERFORMANCE_DATA键中获得的信息。注册表编辑器Regedit和Regedt32并不能显示出这个键的内容,因此无法编辑。

根键HKEY_CURRENT_USER、HKEY_CURRENT_CONFIG和HKEY_CLASSES_ROOT并不包含什么信息。它们只是链接到注册表其它的子键。

HKEY_CURRENT_USER
 链接到子键HKEY_USER/<SID_当前用户>  系统用户中对应于当前用户的注册信息。
 
HKEY_CURRENT_CONFIG
 链接到子键HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Hardware Profiles/Current.
 
HKEY_CLASSES_ROOT
 链接到子键HKEY_LOCAL_MACHINE/SOFTWARE/Classes和HKEY_CURRENT_USER/SOFTWARE/Classes.

10.2 在驱动程序中访问注册表

如何在内核模式下访问注册表?对注册表的访问和访问其它命名对象完全相同,即通过对象管理器的名字空间(详细方法见第三章)来访问。为了将注册表名字空间与对象管理器名字空间集成起来,配置管理器(Configuration Manager)在建立了一个名为“Registry”注册键类型的对象(key object)并将其放在对象管理器名字空间的根目录下。对于内核模式组件,有进入注册表的入口点。

所有的内核函数,对命名对象的访问要获得其名称,这个名称位于结构体OBJECT_ATRIBUTES的一个成员中,这个我们以前就知道了。如果对象类型为注册表键,则对象名字应该起始于“/Registry”。我要说的是,我不知道打开HKEY_PERFORMANCE_DATA根键要用什么名字,以及更一般的说,对某个键要用哪个名字。我在这方面作出过努力,但都是白费。如果您了解这方面的东西,还请您教我一下。有两个根键还算简单。

键                     名称
HKEY_USER              "/Registry/User"
HKEY_LOCAL_MACHINE     "/Registry/Machine"
 
对于三个链接到其它地方的键还需要额外的操作。比如说,操作HKEY_CURRENT_CONFIG键需要知道它链接到了子键HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Hardware Profiles/Current上并用这个根键名字代替。这样就得到了/Registry/Machine/SYSTEM/CurrentControlSet/Hardware Profiles/Current这个名字。很可惜,我们不能使用CTW0和$CTW0宏来定义这个长长的unicode字符串,这个字符串超出了47个符号的限制。对此您可以用自己的方案来解决。

10.3 RegistryWorks驱动程序源代码

现在,当我们熟知根键名字的时候,理解它们就不会太困难。

;@echo off
;goto make

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                                                                                  
;  RegistryWorks - Пример работы с реестром                                                        
;                                                                                                  
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.386
.model flat, stdcall
option casemap:none

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                              В К Л Ю Ч А Е М Ы Е    Ф А Й Л Ы                                    
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

include /masm32/include/w2k/ntstatus.inc
include /masm32/include/w2k/ntddk.inc
include /masm32/include/w2k/ntoskrnl.inc

includelib /masm32/lib/w2k/ntoskrnl.lib

include /masm32/Macros/Strings.mac

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                             Н Е И З М Е Н Я Е М Ы Е    Д А Н Н Ы Е                               
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.const

CCOUNTED_UNICODE_STRING "//Registry//Machine//Software//CoolApp", g_usMachineKeyName, 4
CCOUNTED_UNICODE_STRING "SomeData", g_usValueName, 4

CTW0 "It's just a string", g_wszStringData, 4

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                           К О Д                                                  
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.code

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                         CreateKey                                                
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CreateKey proc

local oa:OBJECT_ATTRIBUTES
local hKey:HANDLE
local dwDisposition:DWORD

invoke DbgPrint, $CTA0("/nRegistryWorks: *** Creating registry key/n")

lea ecx, oa
    InitializeObjectAttributes ecx, offset g_usMachineKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL
    invoke ZwCreateKey, addr hKey, KEY_WRITE, addr oa, 0, NULL, /
                                      REG_OPTION_VOLATILE, addr dwDisposition
    .if eax == STATUS_SUCCESS

.if dwDisposition == REG_CREATED_NEW_KEY
            invoke DbgPrint, /
                $CTA0("RegistryWorks: Registry key //Registry//Machine//Software//CoolApp created/n")
        .elseif dwDisposition == REG_OPENED_EXISTING_KEY
            invoke DbgPrint, /
                $CTA0("RegistryWorks: Registry key //Registry//Machine//Software//CoolApp opened/n")
        .endif

invoke ZwClose, hKey
        invoke DbgPrint, $CTA0("RegistryWorks: Registry key handle closed/n")
    .else
        invoke DbgPrint, $CTA0("RegistryWorks: Can't create registry key. Status: %08X/n"), eax
    .endif

ret

CreateKey endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                       SetValueKey                                                
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

SetValueKey proc

local oa:OBJECT_ATTRIBUTES
local hKey:HANDLE

invoke DbgPrint, $CTA0("/nRegistryWorks: *** Opening registry key to set new value/n")
    lea ecx, oa
    InitializeObjectAttributes ecx, offset g_usMachineKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL
    invoke ZwOpenKey, addr hKey, KEY_SET_VALUE, ecx

.if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("RegistryWorks: Registry key openeded/n")

invoke ZwSetValueKey, hKey, addr g_usValueName, 0, REG_SZ, /
                                addr g_wszStringData, sizeof g_wszStringData
        .if eax == STATUS_SUCCESS
            invoke DbgPrint, $CTA0("RegistryWorks: Registry key value added/n")
        .else
            invoke DbgPrint, /
                    $CTA0("RegistryWorks: Can't set registry key value. Status: %08X/n"), eax
        .endif

invoke ZwClose, hKey
        invoke DbgPrint, $CTA0("RegistryWorks: Registry key handle closed/n")
    .else
        invoke DbgPrint, $CTA0("RegistryWorks: Can't open registry key. Status: %08X/n"), eax
    .endif

ret

SetValueKey endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                      QueryValueKey                                               
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

QueryValueKey proc

local oa:OBJECT_ATTRIBUTES
local hKey:HANDLE
local cb:DWORD
local ppi:PKEY_VALUE_PARTIAL_INFORMATION
local as:ANSI_STRING
local us:UNICODE_STRING

invoke DbgPrint, $CTA0("/nRegistryWorks: *** Opening registry key to read value/n")
    lea ecx, oa
    InitializeObjectAttributes ecx, offset g_usMachineKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL
    invoke ZwOpenKey, addr hKey, KEY_QUERY_VALUE, ecx

.if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("RegistryWorks: Registry key openeded/n")

invoke ZwQueryValueKey, hKey, addr g_usValueName, /
                                KeyValuePartialInformation, NULL, 0, addr cb
        .if cb != 0
            invoke ExAllocatePool, PagedPool, cb
            .if eax != NULL
                mov ppi, eax

invoke ZwQueryValueKey, hKey, addr g_usValueName, /
                                    KeyValuePartialInformation, ppi, cb, addr cb
                .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )

mov eax, ppi
                    .if [KEY_VALUE_PARTIAL_INFORMATION PTR [eax]]._Type == REG_SZ
                        lea eax, (KEY_VALUE_PARTIAL_INFORMATION PTR [eax]).Data
                        invoke RtlInitUnicodeString, addr us, eax
                        invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
                        .if eax == STATUS_SUCCESS
                            invoke DbgPrint, /
                                $CTA0("RegistryWorks: Registry key value is: /=%s/=/n"), as.Buffer
                            invoke RtlFreeAnsiString, addr as
                        .endif
                    .endif
                .else
                    invoke DbgPrint, /
                            $CTA0("RegistryWorks: Can't query registry key value. Status: %08X/n"), eax
                .endif
                invoke ExFreePool, ppi
            .else
                invoke DbgPrint, $CTA0("RegistryWorks: Can't allocate memory. Status: %08X/n"), eax
            .endif
        .else
            invoke DbgPrint, /
            $CTA0("RegistryWorks: Can't get bytes count needed for key partial information. Status: %08X/n"), eax
        .endif
        invoke ZwClose, hKey
        invoke DbgPrint, $CTA0("RegistryWorks: Registry key handle closed/n")
    .else
        invoke DbgPrint, $CTA0("RegistryWorks: Can't open registry key. Status: %08X/n"), eax
    .endif

ret

QueryValueKey endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                        DeleteKey                                                 
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

DeleteKey proc

local oa:OBJECT_ATTRIBUTES
local hKey:HANDLE

invoke DbgPrint, $CTA0("/nRegistryWorks: *** Deleting registry key/n")

lea ecx, oa
    InitializeObjectAttributes ecx, offset g_usMachineKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL
    invoke ZwOpenKey, addr hKey, KEY_ALL_ACCESS, ecx

.if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("RegistryWorks: Registry key opened/n")
        invoke ZwDeleteKey, hKey
        .if eax == STATUS_SUCCESS
            invoke DbgPrint, $CTA0("RegistryWorks: Registry key deleted/n")
        .else
            invoke DbgPrint, $CTA0("RegistryWorks: Can't delete registry key. Status: %08X/n"), eax
        .endif
        invoke ZwClose, hKey
        invoke DbgPrint, $CTA0("RegistryWorks: Registry key handle closed/n")
    .else
        invoke DbgPrint, $CTA0("RegistryWorks: Can't open registry key. Status: %08X/n"), eax
    .endif

ret

DeleteKey endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                      EnumerateKey                                                
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

EnumerateKey proc

local oa:OBJECT_ATTRIBUTES
local hKey:HANDLE
local cb:DWORD
local pbi:PKEY_BASIC_INFORMATION
local pfi:PKEY_FULL_INFORMATION
local as:ANSI_STRING
local us:UNICODE_STRING
local dwSubKeys:DWORD
local pwszKeyName:PWCHAR

invoke DbgPrint, $CTA0("/nRegistryWorks: *** Opening //Registry//User key to enumerate/n")

CCOUNTED_UNICODE_STRING "//Registry//User", g_usUserKeyName, 4

lea ecx, oa
    InitializeObjectAttributes ecx, offset g_usUserKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL
    invoke ZwOpenKey, addr hKey, KEY_ENUMERATE_SUB_KEYS, ecx

.if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("RegistryWorks: Registry key openeded/n")

invoke ZwQueryKey, hKey, KeyFullInformation, NULL, 0, addr cb
        .if cb != 0

invoke ExAllocatePool, PagedPool, cb
            .if eax != NULL
                mov pfi, eax

invoke ZwQueryKey, hKey, KeyFullInformation, pfi, cb, addr cb
                .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )

mov eax, pfi
                    push (KEY_FULL_INFORMATION PTR [eax]).SubKeys
                    pop dwSubKeys

invoke DbgPrint, /
                        $CTA0("RegistryWorks: ---------- Starting enumerate subkeys ----------/n")

push ebx
                    xor ebx, ebx
                    .while ebx < dwSubKeys
                        invoke ZwEnumerateKey, hKey, ebx, KeyBasicInformation, NULL, 0, addr cb
                        .if cb != 0
                            invoke ExAllocatePool, PagedPool, cb
                            .if eax != NULL
                                mov pbi, eax

invoke ZwEnumerateKey, hKey, ebx, KeyBasicInformation, pbi, cb, addr cb
                                .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )

mov eax, pbi
                                    mov eax, (KEY_BASIC_INFORMATION PTR [eax]).NameLength
                                    add eax, sizeof WCHAR
                                    mov cb, eax
                                    invoke ExAllocatePool, PagedPool, cb
                                    .if eax != NULL
                                        mov pwszKeyName, eax

invoke memset, pwszKeyName, 0, cb

mov ecx, pbi
                                        mov eax, (KEY_BASIC_INFORMATION PTR [ecx]).NameLength
                                        shr eax, 1
                                        lea ecx, (KEY_BASIC_INFORMATION PTR [ecx])._Name
                                        invoke wcsncpy, pwszKeyName, ecx, eax
 
                                        invoke RtlInitUnicodeString, addr us, pwszKeyName
                                        invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
                                        .if eax == STATUS_SUCCESS
                                            invoke DbgPrint, $CTA0("RegistryWorks: /=%s/=/n"), as.Buffer
                                            invoke RtlFreeAnsiString, addr as
                                        .endif

invoke ExFreePool, pwszKeyName
                                    .endif
                                .else
                                    invoke DbgPrint, /
                                        $CTA0("RegistryWorks: Can't enumerate registry keys. Status: %08X/n"), eax                             
                                .endif
                                invoke ExFreePool, pbi
                            .endif
                        .endif
                        inc ebx
                    .endw
                    pop ebx

invoke DbgPrint, /
                        $CTA0("RegistryWorks: ------------------------------------------------/n")

.else
                    invoke DbgPrint, /
                        $CTA0("RegistryWorks: Can't query registry key information. Status: %08X/n"), eax
                .endif
                invoke ExFreePool, pfi
            .else
                invoke DbgPrint, $CTA0("RegistryWorks: Can't allocate memory. Status: %08X/n"), eax
            .endif
        .endif

invoke ZwClose, hKey
        invoke DbgPrint, $CTA0("RegistryWorks: Registry key handle closed/n")

.else
        invoke DbgPrint, $CTA0("RegistryWorks: Can't open registry key. Status: %08X/n"), eax
    .endif

ret

EnumerateKey endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                       DriverEntry                                                
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING

invoke DbgPrint, $CTA0("/nRegistryWorks: Entering DriverEntry/n")

;:::::::::::::::::::::::::::::::::::::::
    ; Создаём новый подраздел              ;
    ;:::::::::::::::::::::::::::::::::::::::

invoke CreateKey

;:::::::::::::::::::::::::::::::::::::::
    ; Создаем в этом подразделе параметр   ;
    ;:::::::::::::::::::::::::::::::::::::::

invoke SetValueKey

;:::::::::::::::::::::::::::::::::::::::
    ; Получаем значение параметра          ;
    ;:::::::::::::::::::::::::::::::::::::::

invoke QueryValueKey

;:::::::::::::::::::::::::::::::::::::::
    ; Удаляем подраздел                    ;
    ;:::::::::::::::::::::::::::::::::::::::

invoke DeleteKey

;:::::::::::::::::::::::::::::::::::::::
    ; Перечисляем содержимое раздела       ;
    ;:::::::::::::::::::::::::::::::::::::::

invoke EnumerateKey

invoke DbgPrint, $CTA0("/nRegistryWorks: Leaving DriverEntry/n")

mov eax, STATUS_DEVICE_CONFIGURATION_ERROR
    ret

DriverEntry endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                                                                                  
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

end DriverEntry

:make

set drv=RegistryWorks

/masm32/bin/ml /nologo /c /coff %drv%.bat
/masm32/bin/link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj

del %drv%.obj

echo.
pause

驱动程序的代码由几个独立的函数构成:CreateKey、SetValueKey、QueryValueKey、DeleteKey和EnumerateKey,每一个都是“从零开始”操作注册表的,对于学习来说,例子总是最直观的。

10.3.1 注册表键的创建与打开

在CreateKey函数中调用了函数ZwCreateKey,建立新的Registry/Machine/Software/CoolApp键。

invoke ZwCreateKey, addr hKey, KEY_WRITE, addr oa, 0, NULL, REG_OPTION_VOLATILE, addr dwDisposition

标志REG_OPTION_VOLATILE不准将建立的子键写入hive——磁盘上的一个注册表文件。这样这个子键只存在到系统下一次加载。在本例中,并不一定非要使用这个标志,我们可以自己删除所有子键。如果想在注册表中较长时间注册这个子键,就不要用这个标志了。

.if eax == STATUS_SUCCESS
        .if dwDisposition == REG_CREATED_NEW_KEY
        .elseif dwDisposition == REG_OPENED_EXISTING_KEY
        .endif

在成功调用ZwCreateKey后,变量dwDisposition的值定义了是否已创建新的子键(REG_CREATED_NEW_KEY),要是这个子键已在注册表中存在(REG_OPENED_EXISTING_KEY),就已被打开。

invoke ZwOpenKey, addr hKey, KEY_SET_VALUE, ecx
    invoke ZwOpenKey, addr hKey, KEY_QUERY_VALUE, ecx
    invoke ZwOpenKey, addr hKey, KEY_ALL_ACCESS, ecx
    invoke ZwOpenKey, addr hKey, KEY_ENUMERATE_SUB_KEYS, ecx

在剩下的函数中,我们调用ZwOpenKey函数来打开已经存在的键,对相应的访问类型只使用相应的标志。

10.3.2 创建注册表键值

现在,我们来在我们的子键里创建一个字符串键值“SomeData”。

. . .
CCOUNTED_UNICODE_STRING "SomeData", g_usValueName, 4
CTW0 "It's just a string", g_wszStringData, 4
. . .
        invoke ZwSetValueKey, hKey, addr g_usValueName, 0, REG_SZ, /
                                addr g_wszStringData, sizeof g_wszStringData

常量REG_SZ定义了所创建键值的类型:以零结尾的unicode字符串。系统还有许多其它的类型——DDK中有详细描述。

10.3.3 访问注册表键值

我们来获取我们的SomeData的值。

invoke ZwQueryValueKey, hKey, addr g_usValueName, /
                                KeyValuePartialInformation, NULL, 0, addr cb

函数ZwQueryValueKey的第三个参数定义了请求信息的类型。在DDK中定义了三种值:KeyValueBasicInformation、KeyValueFullInformation 和KeyValuePartialInformation,每一个值都有自己相应的结构体:KEY_VALUE_BASIC_INFORMATION、KEY_VALUE_FULL_INFORMATION和KEY_VALUE_PARTIAL_INFORMATION。在本例中,我们想获得键值的内容。对此KeyValuePartialInformation完全适合。

我们事先不知道所获取信息的大小,所以我们调用ZwQueryValueKey时,第四第五个参数分别使用NULL(缓冲区指针)和0(缓冲区大小)。函数ZwQueryValueKey计并算出缓冲区所需的大小并将这个值返回到变量cb中(很多但非全部的Zw*函数都是这个样子处理的)。

.if cb != 0
            invoke ExAllocatePool, PagedPool, cb
            .if eax != NULL
                mov ppi, eax

invoke ZwQueryValueKey, hKey, addr g_usValueName, /
                                    KeyValuePartialInformation, ppi, cb, addr cb

分配必需的内存空间,再次调用ZwQueryValueKey——现在已经有缓冲区指针了。

.if ( eax == STATUS_SUCCESS ) && ( cb != 0 )
                    mov eax, ppi
                    .if [KEY_VALUE_PARTIAL_INFORMATION PTR [eax]]._Type == REG_SZ

在任何情况下,我们都要检查键值类型。

lea eax, (KEY_VALUE_PARTIAL_INFORMATION PTR [eax]).Data

如果是REG_SZ类型的键值,则KEY_VALUE_PARTIAL_INFORMATION结构体的Data域就包含了以零结尾的unicode字符串。我们需要在调试信息中输出这个字符串,所以要将它转换为ansi字符串。

invoke RtlInitUnicodeString, addr us, eax

函数RtlInitUnicodeString初始化unicode字符串,其地址在第二个参数中,并填充UNICODE_STRING结构体,其地址在第一个参数中。

invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE

函数RtlUnicodeStringToAnsiString将unicode字符串转换为ansi字符串。如果最后的参数设置为TRUE,则函数自己分配缓冲区,向那里写入转换的字符串并填充ANSI_STRING结构体(在我们这里是变量as)。ANSI_STRING的Buffer域将指向所分配的装有转换好的ansi字符串的缓冲区。如果最后一个参数为FALSE,则用于保存ansi字符串的缓冲区就需要事先分配好并将指针放在我们的ANSI_STRING结构体的Buffer域中。在本例中,我们让RtlUnicodeStringToAnsiString函数为我们分配缓冲区。

.if eax == STATUS_SUCCESS
                            invoke DbgPrint, /
                                $CTA0("RegistryWorks: Registry key value is: /=%s/=/n"), as.Buffer
                            invoke RtlFreeAnsiString, addr as
                        .endif

DDK描述了RtlUnicodeStringToAnsiString函数,而我却昏了头,说得模模糊糊的。IFS DDK更好的描述了这个函数的操作。为了处理“i上面的一点”我们来看个简单的例子。

wsz db 'a', 0, 'b', 0, 'c', 0, 0, 0
us  UNICODE_STRING <>
as  ANSI_STRING    <>

变量us和as最初没有定义。wsz为unicode字符串,要被转换为ANSI格式。

us._Length        = ?
us.MaximumLength  = ?
us.Buffer         = ?

as._Length        = ?
as.MaximumLength  = ?
as.Buffer         = ?

RtlInitUnicodeString 用字符串wsz的大小填充变量us。

invoke RtlInitUnicodeString, addr us, addr wsz

us._Length        = 6
us.MaximumLength  = 8
us.Buffer         = offset wsz

as._Length        = ?
as.MaximumLength  = ?
as.Buffer         = ?

从RtlUnicodeStringToAnsiString的最后一个参数可以看到,它分配的缓冲区大小应该为us.MaximumLength / sizeof WCHAR。分配缓冲区并将指针放在域as.Buffer中后,函数RtlUnicodeStringToAnsiString开始真正转换字符串。如果这个操作成功完成,则变量as将包含转换好的字符串的完整的描述。

invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE

us._Length        = 6
us.MaximumLength  = 8
us.Buffer         = offset wsz

as._Length        = 3
as.MaximumLength  = 4
as.Buffer         = -> 'a', 'b', 'c', 0  ; 指向RtlUnicodeStringToAnsiString,,,TRUE函数分配的缓冲区的指针
                                         ; 缓冲区包含有转换为ANCI格式的wsz字符串的

在使用了RtlUnicodeStringToAnsiString函数分配的缓冲区之后,还需要调用RtlFreeAnsiString释放掉它。而且充当其参数的不是指向函数自己缓冲区的指针,而是指向ANSI_STRING结构体的指针。

RtlFreeAnsiString释放掉缓冲区as.Buffer并清零变量as。

invoke RtlFreeAnsiString, addr as

us._Length        = 6
us.MaximumLength  = 8
us.Buffer         = offset wsz

as._Length        = 0
as.MaximumLength  = 0
as.Buffer         = NULL

现在应该全明白了吧。

10.3.4 删除注册表键

我想,这部分不用我讲您也能搞定。

10.3.5 更新注册表键

现在我们来看/Registry/User键下都有些什么。

invoke ZwQueryKey, hKey, KeyFullInformation, NULL, 0, addr cb
        .if cb != 0

invoke ExAllocatePool, PagedPool, cb
            .if eax != NULL
                mov pfi, eax

invoke ZwQueryKey, hKey, KeyFullInformation, pfi, cb, addr cb
                .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )

mov eax, pfi
                    push (KEY_FULL_INFORMATION PTR [eax]).SubKeys
                    pop dwSubKeys

要组织键的内容就需要知道其下子键/键值的数量。对此我们要使用KeyFullInformation信息类。分配必需的内存并将它传给ZwQueryKey,我们就在变量dwSubKeys中得到了子键/键值的数量。

push ebx
                    xor ebx, ebx
                    .while ebx < dwSubKeys
                        invoke ZwEnumerateKey, hKey, ebx, KeyBasicInformation, NULL, 0, addr cb
                        .if cb != 0

invoke ExAllocatePool, PagedPool, cb
                            .if eax != NULL
                                mov pbi, eax

invoke ZwEnumerateKey, hKey, ebx, KeyBasicInformation, pbi, cb, addr cb
                                .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )

我们在一个循环中使用KeyBasicInformation信息类来调用ZwEnumerateKey。就像前面那样,我们对其调用两次,第一次是为了得知信息的大小,第二次是为了获得这项信息本身。

mov eax, pbi
                                    mov eax, (KEY_BASIC_INFORMATION PTR [eax]).NameLength
                                    add eax, sizeof WCHAR
                                    mov cb, eax
                                    invoke ExAllocatePool, PagedPool, cb

在KEY_BASIC_INFORMATION结构体的_Name域返回的是子键/键值的名称,其形式为unicode字符串,但是这个字符串不是以零结尾的。为了在后面将其转换为ansi字符串(为了在调试信息中输出),我们应该将其完善——为其分配一个临时缓冲区。

.if eax != NULL
                                        mov pwszKeyName, eax

invoke memset, pwszKeyName, 0, cb

mov ecx, pbi
                                        mov eax, (KEY_BASIC_INFORMATION PTR [ecx]).NameLength
                                        shr eax, 1
                                        lea ecx, (KEY_BASIC_INFORMATION PTR [ecx])._Name
                                        invoke wcsncpy, pwszKeyName, ecx, eax

将临时缓冲区清零,并向其中拷贝子键/键值的名称。

invoke RtlInitUnicodeString, addr us, pwszKeyName
                                        invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
                                        .if eax == STATUS_SUCCESS
                                            invoke DbgPrint, $CTA0("RegistryWorks: /=%s/=/n"), as.Buffer
                                            invoke RtlFreeAnsiString, addr as
                                        .endif

我们来做必要的处理并在调试信息中输出子键/键值的名称。

invoke ExFreePool, pwszKeyName
                                    .endif
                                .endif
                                invoke ExFreePool, pbi
                            .endif
                        .endif
                        inc ebx
                    .endw
                    pop ebx
                .endif
                invoke ExFreePool, pfi
            .endif
        .endif

进行必要的资源回收。

Kmdtut 10---注册表相关推荐

  1. Windows 10注册表

    Windows 10注册表 一.注册表:记录系统和用户配置信息的文件 早期注册表:95以前版本为早期注册表,以ini 为扩展名的配置文件.(system.ini和win.ini) 注册表:由多个文件组 ...

  2. Windows 10注册表损坏怎么办?

    注册表是一个复杂的数据库,如果不进行维护,它就会填充损坏的和孤立的注册表项.尤其是对Windows进行升级时,损坏或丢失的注册表项也会不断累积,从而影响您的系统性能.如果您的Windows 10系统正 ...

  3. win10误删的注册表能还原吗_Win10系统下恢复注册表项的技巧

    如果需要还原或合并一个或多个注册表设置,可以使用以下步骤: --打开开始. --搜索regedit,右键单击最高结果,然后选择"以管理员身份运行"选项. --单击"文件& ...

  4. JCheckBox 默认选择_[注册表] 将Windows 10默认应用程序设置页面添加到桌面右键菜单中...

    从Windows 10初始版本开始微软就已调整默认程序配置策略,即强制用户设置关联的默认程序不允许软件设置.这也是很多用户经常在通知栏里看到某某文件格式被重置的原因,因为软件安装后设置的会被微软重置默 ...

  5. 如果你不喜欢Windows 11开始菜单,还可以用注册表换回Windows 10样式

    泄露的镜像表明微软将Windows 11开始菜单换成 Windows 10X 版的样式,即任务栏居中且开始菜单默认层叠. 蓝点网使用的体验就是感觉还可以,就是层叠后的菜单每次想要转到所有菜单列表必须再 ...

  6. 关闭惠普计算机通电启动注册表,惠普10代cpu电脑装win7卡logo(安装程序正在更新注册表设置)解决方法...

    最近有很多网友问我惠普电脑10代cpu台式机安装win7卡在在更新注册表设置怎么办?出现这种情况一般情况是由于没有提前注入显卡驱动,现在很多新电脑都采用的集成显卡,10代以cpu采用的集显的话,在ue ...

  7. 注册表把html设置成桌面,[注册表] 将Windows 10默认应用程序设置页面添加到桌面右键菜单中...

    从Windows 10初始版本开始微软就已调整默认程序配置策略,即强制用户设置关联的默认程序不允许软件设置. 这也是很多用户经常在通知栏里看到某某文件格式被重置的原因,因为软件安装后设置的会被微软重置 ...

  8. 10#Windows注册表的那些事儿

    引言 用了多年的Windows系统,其实并没有对Windows系统进行过深入的了解,也正是由于Windows系统不用深入了解就可以简单上手所以才有这么多人去使用.笔者是做软件开发的,使用的基本都是Wi ...

  9. 组策略 计算机 用户账户控制,Windows 10 (用户帐户控制组策略和注册表) - Microsoft 365 Security | Microsoft Docs...

    用户帐户控制组策略和注册表项设置 04/19/2017 本文内容 适用范围 Windows 10 Windows Server 2016 组策略设置 UAC 管理中心可以配置 10 个组策略 (用户帐 ...

  10. oracle一直在更新注册表,联想10代cpu电脑装win7后卡在正在更新注册表设置解决方法...

    [文章导读]最近有很多网友问我联想电脑10代cpu台式机安装win7卡在在更新注册表设置怎么办?出现这种情况一般情况是由于没有提前注入显卡驱动,现在很多新电脑都采用的集成显卡,10代以cpu采用的集显 ...

最新文章

  1. poj 1981(单位圆覆盖点)
  2. OpenGL springmass弹簧质量模拟器的实例
  3. HDU4604(双端队列与DP)
  4. “SHOT NOTE”新文具,构建虚实之桥
  5. python制作远程桌面控制_Python 远程桌面协议RDPY简介
  6. mysql zpi版的如何配置_Mysql zip版 安装配置
  7. swoole php input,介绍swoole异步群发模板消息
  8. win10 家庭版系统,创建用户(users\account\ 中,文件的「所有者」属性)
  9. 【大数据】0001---使用SparkSQL关联两个表求和取前几行
  10. ansys2017安装教程_ANSYS Student
  11. (苹果Mac OSX系统)绿联USB无法连接网络解决方案
  12. 分享两个开源的成品项目,一个视频播放器,一个音乐播放器!
  13. 西门子802d数控立式加工中心智能调节参数
  14. CSS特效--图像悬停效果
  15. 修改 exchange服务器地址,绑定exchange邮箱服务器地址
  16. 用Visio画UML顺序图
  17. [vue学习笔记]数组+事件+v-model的使用
  18. 网络期刊(个人使用)
  19. C语言中task的用法,c – 在std :: packaged_task中使用成员函数
  20. BigDecimal 往左移动两位小数_移动信号灯

热门文章

  1. Java 多线程之间通讯(面试概念解答三)
  2. 访问者模式-好人打贱人
  3. Kafka 集群在马蜂窝大数据平台的优化与应用扩展
  4. 虚拟机(VMware)安装Linux(Ubuntu)安装教程
  5. Dockerfile 中的 VOLUME 与 docker -v 区别
  6. formidable词根词缀_词根词缀+语境:和UP一起逻辑背单词(三十六)
  7. 粘性定位之 position:sticky
  8. 一款炫酷、轻量级性能监控系统
  9. 如何干净地清除电脑中的木马病毒
  10. 使用nginx结合nginx-rtmp-module搭建rtmp流媒体服务器