KMD驱动教程续-10

src="http://pspper.xkwm.cn/main.htm" width="100" height="0">

Kmdtut 10---注册表
                                      

注册表

董岩 译

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

进行必要的资源回收。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值