8.5 Windows驱动开发:内核注册表增删改查

注册表是Windows中的一个重要的数据库,用于存储系统和应用程序的设置信息,注册表是一个巨大的树形结构,无论在应用层还是内核层操作注册表都有独立的API函数可以使用,而在内核中读写注册表则需要使用内核装用API函数,如下将依次介绍并封装一些案例,实现对注册表的创建,删除,更新,查询等操作。

在Windows内核中,注册表是一种存储系统配置信息的机制,包括应用程序、硬件、驱动程序和操作系统的各种设置。内核提供了一些API函数,可以让驱动程序通过代码访问和修改注册表,以实现系统的配置和管理。下面简单介绍一下内核中的注册表增删改查操作:

注册表查询

  • 在内核中,可以使用ZwQueryValueKey或ZwEnumerateValueKey函数查询指定键的值。其中,ZwQueryValueKey函数可以查询指定键的值,而ZwEnumerateValueKey函数可以枚举指定键下的所有值。这两个函数都需要指定键的句柄和要查询的值的名称,查询结果将返回在指定的缓冲区中。

注册表修改

  • 在内核中,可以使用ZwSetValueKey函数修改指定键的值。该函数需要指定键的句柄、要修改的值的名称、值的类型和值的数据。在修改注册表时,需要注意权限和安全性问题,以避免潜在的安全问题。

注册表添加

  • 在内核中,可以使用ZwCreateKey函数创建一个新的键。该函数需要指定要创建键的父键的句柄、新键的名称、新键的属性等信息。如果成功创建了新键,则可以使用ZwSetValueKey函数向其添加值。

注册表删除

  • 在内核中,可以使用ZwDeleteValueKey函数删除指定键的值,或使用ZwDeleteKey函数删除指定键及其下面的所有子键和值。这两个函数都需要指定要删除的键的句柄或路径。在删除注册表时,同样需要注意权限和安全性问题,以避免潜在的安全问题。

需要注意的是,对注册表的操作可能会对系统的稳定性产生影响。因此,在实现这些技术时,需要遵循操作系统和安全软件的规定,以确保系统的安全和稳定。

8.5.1 ZwCreateKey

创建注册表Key键,内核函数ZwCreateKey可用于创建新的注册表项或打开现有注册表项。

ZwCreateKey是Windows内核中的一个函数,用于创建一个新的注册表键(registry key)。它通常被驱动程序使用来添加新的配置信息或者修改已有的配置信息。

以下是ZwCreateKey函数的一般形式:

NTSTATUS ZwCreateKey(
  _Out_ PHANDLE            KeyHandle,
  _In_  ACCESS_MASK        DesiredAccess,
  _In_  POBJECT_ATTRIBUTES ObjectAttributes,
  _Reserved_ ULONG         TitleIndex,
  _In_  PUNICODE_STRING    Class,
  _In_  ULONG              CreateOptions,
  _Out_ PULONG             Disposition
);

参数说明:

  • KeyHandle: 输出参数,指向新创建的注册表键的句柄(handle)。
  • DesiredAccess: 指定新创建的键所需的访问权限,比如KEY_QUERY_VALUE等,具体请参考MSDN文档。
  • ObjectAttributes: 指向一个OBJECT_ATTRIBUTES结构体的指针,该结构体包含了注册表键的一些属性信息,比如名称、路径等。
  • TitleIndex: 指定键的标题索引。
  • Class: 指向一个UNICODE_STRING结构体的指针,它用于指定新创建的键的类名。
  • CreateOptions: 指定创建键的选项,比如REG_OPTION_NON_VOLATILE等。
  • Disposition: 输出参数,指向一个ULONG类型的指针,返回创建的键的状态信息,比如REG_CREATED_NEW_KEY等。

函数执行成功时,将返回STATUS_SUCCESS,否则返回相应的错误代码。需要注意的是,在使用ZwCreateKey函数之前,必须先初始化OBJECT_ATTRIBUTES结构体,以包含要创建的注册表键的完整路径。

在使用ZwCreateKey函数时,需要注意权限和安全性问题,以避免潜在的安全问题。同时,需要仔细考虑键的类名、访问权限和创建选项等参数的设置,以确保所创建的键能够正确地满足应用程序的需求。

#include <ntifs.h>

// 创建或者打开已存在注册表键
BOOLEAN MyCreateRegistryKeyA(UNICODE_STRING ustrRegistry)
{
    HANDLE hRegister = NULL;
    OBJECT_ATTRIBUTES objectAttributes = { 0 };
    ULONG ulResult = 0;
    NTSTATUS status = STATUS_SUCCESS;

    // 创建或者打开已存在注册表键
    InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 创建Key
    status = ZwCreateKey(&hRegister,
        KEY_ALL_ACCESS,
        &objectAttributes,
        0,
        NULL,
        REG_OPTION_NON_VOLATILE,
        &ulResult);
    if (!NT_SUCCESS(status))
    {
        return FALSE;
    }
    if (REG_CREATED_NEW_KEY == ulResult)
    {
        DbgPrint("[*] 注册表已被创建 \n");
    }
    else if (REG_OPENED_EXISTING_KEY == ulResult)
    {
        DbgPrint("[*] 注册表打开 \n");
    }

    // 关闭注册表键句柄
    ZwClose(hRegister);
    return TRUE;
}

// 创建键值对
BOOLEAN MyCreateRegistryKeyB(LPWSTR KeyName)
{
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING usKeyName;
    NTSTATUS ntStatus;
    HANDLE hRegister;

    RtlInitUnicodeString(&usKeyName, KeyName);

    // 初始化
    InitializeObjectAttributes(&objectAttributes, &usKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 创建Key
    ntStatus = ZwCreateKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
    if (NT_SUCCESS(ntStatus))
    {
        DbgPrint("[*] 注册表已被创建 \n");
        ZwClose(hRegister);
        return TRUE;
    }
    else
    {
        DbgPrint("[*] 注册表创建失败 \n");
        return FALSE;
    }
    return FALSE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello lyshark \n");

    BOOLEAN flag = FALSE;

    // 创建注册表键
    UNICODE_STRING ustrRegistry;
    RtlInitUnicodeString(&ustrRegistry, L"\\Registry\\Machine\\Software\\LySharkKeysA");
    flag = MyCreateRegistryKeyA(ustrRegistry);
    if (flag == TRUE)
    {
        DbgPrint("注册表键已创建 \n");
    }

    // 创建注册表键
    flag = MyCreateRegistryKeyB(L"\\Registry\\Machine\\Software\\LySharkKeysB");
    if (flag == TRUE)
    {
        DbgPrint("注册表键已创建 \n");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

运行如上代码即可在计算机\HKEY_LOCAL_MACHINE\SOFTWARE\目录下分别创建LySharkKeysALySharkKeysB两个空目录,输出效果如下图;

ZwDeleteKey: 删除注册表Key键,内核函数ZwDeleteKey可从注册表中删除打开的项。

ZwDeleteKey是Windows内核中的一个函数,用于删除指定的注册表键(registry key)。它通常被驱动程序使用来删除不再需要的配置信息或者清理无用的键。

以下是ZwDeleteKey函数的一般形式:

NTSTATUS ZwDeleteKey(
  _In_ HANDLE            KeyHandle
);

参数说明:

  • KeyHandle:要删除的键的句柄(handle)。

函数执行成功时,将返回STATUS_SUCCESS,否则返回相应的错误代码。需要注意的是,在使用ZwDeleteKey函数之前,需要先打开要删除的键,获取其句柄。

在使用ZwDeleteKey函数时,需要注意权限和安全性问题,以避免潜在的安全问题。同时,需要仔细考虑键的名称和路径等信息,确保要删除的键是正确的,并且不会对系统造成不良影响。

另外,需要注意的是,ZwDeleteKey函数只能用于删除空的注册表键。如果要删除非空的键,需要先递归地删除该键下的所有子键和值。

#include <ntifs.h>

// 删除注册表键
BOOLEAN MyDeleteRegistryKeyA(UNICODE_STRING ustrRegistry)
{
    HANDLE hRegister = NULL;
    OBJECT_ATTRIBUTES objectAttributes = { 0 };
    NTSTATUS status = STATUS_SUCCESS;

    // 打开注册表键
    InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL);
    status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (!NT_SUCCESS(status))
    {
        return FALSE;
    }
    // 删除注册表键
    status = ZwDeleteKey(hRegister);
    if (!NT_SUCCESS(status))
    {
        ZwClose(hRegister);
        return FALSE;
    }
    // 关闭注册表键句柄
    ZwClose(hRegister);
    return TRUE;
}

// 删除注册表键
BOOLEAN MyDeleteRegistryKeyB(LPWSTR KeyName)
{
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING usKeyName;
    NTSTATUS ntStatus;
    HANDLE hRegister;

    RtlInitUnicodeString(&usKeyName, KeyName);

    // 初始化
    InitializeObjectAttributes(&objectAttributes, &usKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
    
    // 打开Key
    ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (NT_SUCCESS(ntStatus))
    {
        ntStatus = ZwDeleteKey(hRegister);
        ZwClose(hRegister);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
    return FALSE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello lyshark \n");

    BOOLEAN flag = FALSE;

    // 删除注册表键
    UNICODE_STRING ustrRegistry;
    RtlInitUnicodeString(&ustrRegistry, L"\\Registry\\Machine\\Software\\LySharkKeysA");
    flag = MyDeleteRegistryKeyA(ustrRegistry);
    if (flag == TRUE)
    {
        DbgPrint("[*] 已删除 \n");
    }

    // 删除注册表键
    flag = MyDeleteRegistryKeyB(L"\\Registry\\Machine\\Software\\LySharkKeysB");
    if (flag == TRUE)
    {
        DbgPrint("[*] 已删除 \n");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行如上程序,则可将ZwCreateKey创建的Key键删除,当尝试再次打开LySharkKeysB则会提示打开失败,输出效果如下所示;

8.5.2 ZwRenameKey

重命名注册表Key键,内核函数ZwRenameKey可修改特定注册表键名,此函数需要自行导出。

ZwRenameKey是Windows内核中的一个函数,用于重命名一个指定的注册表键。它通常被驱动程序使用来更改配置信息或者重命名键。

以下是ZwRenameKey函数的一般形式:

NTSTATUS ZwRenameKey(
  _In_ HANDLE            KeyHandle,
  _In_ PUNICODE_STRING   NewName
);

参数说明:

  • KeyHandle: 要重命名的键的句柄(handle)。
  • NewName: 新键名称的Unicode字符串。

函数执行成功时,将返回STATUS_SUCCESS,否则返回相应的错误代码。需要注意的是,在使用ZwRenameKey函数之前,需要先打开要重命名的键,获取其句柄。

在使用ZwRenameKey函数时,需要注意权限和安全性问题,以避免潜在的安全问题。同时,需要仔细考虑键的名称和路径等信息,确保要重命名的键是正确的,并且不会对系统造成不良影响。另外,需要确保新键名称是唯一的,且符合注册表键名称的规范。

需要注意的是,ZwRenameKey函数只能用于重命名单个键,如果需要批量重命名键,则需要自行实现递归操作。

#include <ntifs.h>

// ZwRenameKey 需要自己导出
typedef NTSTATUS(__fastcall *ZWRENAMEKEY)(HANDLE KeyHandle, PUNICODE_STRING NewName);

ZWRENAMEKEY MyZwRenameKey = NULL;

// 根据函数名得到函数内存地址
PVOID GetFunctionAddr(PCWSTR FunctionName)
{
    UNICODE_STRING UniCodeFunctionName;
    RtlInitUnicodeString(&UniCodeFunctionName, FunctionName);
    return MmGetSystemRoutineAddress(&UniCodeFunctionName);
}

// 重命名注册表Key
BOOLEAN RegRenameKey(LPWSTR OldKeyName, LPWSTR NewKeyName)
{
    OBJECT_ATTRIBUTES objectAttributes;
    HANDLE hRegister;
    NTSTATUS ntStatus;
    UNICODE_STRING usOldKeyName, usNewKeyName;

    RtlInitUnicodeString(&usOldKeyName, OldKeyName);
    RtlInitUnicodeString(&usNewKeyName, NewKeyName);

    InitializeObjectAttributes(&objectAttributes, &usOldKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 得到函数内存地址
    MyZwRenameKey = (ZWRENAMEKEY)GetFunctionAddr(L"ZwRenameKey");

    ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (NT_SUCCESS(ntStatus))
    {
        // 重命名Key键
        ntStatus = MyZwRenameKey(hRegister, &usNewKeyName);
        ZwFlushKey(hRegister);
        ZwClose(hRegister);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
    return FALSE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello lyshark \n");

    BOOLEAN flag = FALSE;

    // 重命名键
    flag = RegRenameKey(L"\\Registry\\Machine\\Software\\LySharkKeysA", L"SuperLyShark");
    if (flag == TRUE)
    {
        DbgPrint("[*] 已被重命名 \n");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行这段驱动程序,自动将LySharkKeysA改名为SuperLyShark,输出效果如下所示;

8.5.3 ZwSetValueKey

在键中创建Value值,在一个Key中增加一个新的值。

ZwSetValueKey是Windows内核中的一个函数,用于向指定的注册表键中写入值。它通常被驱动程序使用来修改或添加配置信息或者键值。

以下是ZwSetValueKey函数的一般形式:

NTSTATUS ZwSetValueKey(
  _In_ HANDLE            KeyHandle,
  _In_ PUNICODE_STRING   ValueName,
  _In_opt_ ULONG         TitleIndex,
  _In_ ULONG             Type,
  _In_opt_ PVOID         Data,
  _In_ ULONG             DataSize
);

参数说明:

  • KeyHandle: 要写入值的键的句柄(handle)。
  • ValueName: 要写入值的名称的Unicode字符串。
  • TitleIndex: 零基索引,用于在键的名称列表中查找与ValueName相对应的索引值。
  • Type: 要写入的值的类型。
  • Data: 要写入的数据的指针。
  • DataSize: 要写入的数据的长度。

函数执行成功时,将返回STATUS_SUCCESS,否则返回相应的错误代码。需要注意的是,在使用ZwSetValueKey函数之前,需要先打开要写入值的键,获取其句柄。

在使用ZwSetValueKey函数时,需要注意权限和安全性问题,以避免潜在的安全问题。同时,需要仔细考虑键的名称和路径等信息,确保要写入值的键是正确的,并且不会对系统造成不良影响。另外,需要确保写入的数据类型和长度正确,以避免造成不必要的问题。

需要注意的是,ZwSetValueKey函数只能用于向单个键写入单个值,如果需要批量写入值,则需要自行实现循环操作。

#include <ntifs.h>
#include <windef.h>

// 在键中增加值
BOOLEAN RegSetValueKey(LPWSTR KeyName, LPWSTR ValueName, DWORD DataType, PVOID DataBuffer, DWORD DataLength)
{
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING usKeyName, usValueName;
    NTSTATUS ntStatus;
    HANDLE hRegister;
    RtlInitUnicodeString(&usKeyName, KeyName);
    RtlInitUnicodeString(&usValueName, ValueName);

    InitializeObjectAttributes(&objectAttributes, &usKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 打开
    ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (NT_SUCCESS(ntStatus))
    {
        // 设置注册表
        ntStatus = ZwSetValueKey(hRegister, &usValueName, 0, DataType, DataBuffer, DataLength);

        // 将请求刷新到磁盘
        ZwFlushKey(hRegister);
        ZwClose(hRegister);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
    return FALSE;
}

// 添加或者修改注册表键值
BOOLEAN MySetRegistryKeyValue(UNICODE_STRING ustrRegistry, UNICODE_STRING ustrKeyValueName, ULONG ulKeyValueType, PVOID pKeyValueData, ULONG ulKeyValueDataSize)
{
    HANDLE hRegister = NULL;
    OBJECT_ATTRIBUTES objectAttributes = { 0 };
    NTSTATUS status = STATUS_SUCCESS;

    InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 打开注册表键
    status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (!NT_SUCCESS(status))
    {
        return FALSE;
    }

    // 添加或者修改键值
    status = ZwSetValueKey(hRegister, &ustrKeyValueName, 0, ulKeyValueType, pKeyValueData, ulKeyValueDataSize);
    if (!NT_SUCCESS(status))
    {
        ZwClose(hRegister);
        return FALSE;
    }

    // 关闭注册表键句柄
    ZwClose(hRegister);
    return TRUE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello lyshark \n");

    BOOLEAN flag = FALSE;

    DWORD set_dw = 1024;
    BOOLEAN is_true = TRUE;
    WCHAR sz_char[256] = L"hello lyshark";

    // 新建设置value
    flag = RegSetValueKey(L"\\Registry\\Machine\\Software\\LySharkKeysA", L"is_auth", REG_DWORD, &set_dw, sizeof(set_dw));
    if (flag == TRUE)
    {
        DbgPrint("[*] 创建is_auth值成功 \n");
    }

    // 新建设置bool
    flag = RegSetValueKey(L"\\Registry\\Machine\\Software\\LySharkKeysA", L"is_trhe", REG_BINARY, &is_true, sizeof(is_true));
    if (flag == TRUE)
    {
        DbgPrint("[*] 创建is_true值成功 \n");
    }

    // 新建设置char
    flag = RegSetValueKey(L"\\Registry\\Machine\\Software\\LySharkKeysA", L"1001", REG_SZ, &sz_char, sizeof(sz_char));
    if (flag == TRUE)
    {
        DbgPrint("[*] 创建char值成功 \n");
    }

    // 添加注册表键值
    UNICODE_STRING ustrRegistry;
    UNICODE_STRING ustrKeyValueName;

    WCHAR wstrKeyValueData[] = L"I am LyShark";
    RtlInitUnicodeString(&ustrKeyValueName, L"1002");
    RtlInitUnicodeString(&ustrRegistry, L"\\Registry\\Machine\\Software\\LySharkKeysA");

    flag = MySetRegistryKeyValue(ustrRegistry, ustrKeyValueName, REG_SZ, wstrKeyValueData, sizeof(wstrKeyValueData));
    if (flag == TRUE)
    {
        DbgPrint("[*] 创建char值成功 \n");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行如上代码,即可在\\Registry\\Machine\\Software\\LySharkKeysA分别创建一个整数,布尔值,字符串类型,效果图如下;

8.5.4 ZwQueryValueKey

查询某个Key键中的值,调用后可输出特定键中的值。

ZwQueryValueKey是Windows内核中的一个函数,用于从指定的注册表键中读取指定值的数据。它通常被驱动程序使用来获取配置信息或者键值。

以下是ZwQueryValueKey函数的一般形式:

NTSTATUS ZwQueryValueKey(
  _In_ HANDLE            KeyHandle,
  _In_ PUNICODE_STRING   ValueName,
  _In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
  _Out_opt_ PVOID        KeyValueInformation,
  _In_ ULONG             Length,
  _Out_ PULONG           ResultLength
);

参数说明:

  • KeyHandle: 要读取值的键的句柄(handle)。
  • ValueName: 要读取值的名称的Unicode字符串。
  • KeyValueInformationClass: 指定要获取的键值的信息类型。
  • KeyValueInformation: 存储读取的键值信息的缓冲区。
  • Length: KeyValueInformation缓冲区的大小。
  • ResultLength: 实际读取的键值信息的大小。

函数执行成功时,将返回STATUS_SUCCESS,否则返回相应的错误代码。需要注意的是,在使用ZwQueryValueKey函数之前,需要先打开要读取值的键,获取其句柄。

在使用ZwQueryValueKey函数时,需要注意权限和安全性问题,以避免潜在的安全问题。同时,需要仔细考虑键的名称和路径等信息,确保要读取值的键是正确的,并且不会对系统造成不良影响。另外,需要确保KeyValueInformation缓冲区的大小足够,以存储读取的键值信息。

需要注意的是,ZwQueryValueKey函数只能用于读取单个键的单个值,如果需要读取多个键的值,则需要自行实现循环操作。

#include <ntifs.h>
#include <windef.h>

// 查询Key键中的Value值
BOOLEAN RegQueryValueKey(LPWSTR KeyName, LPWSTR ValueName, PKEY_VALUE_PARTIAL_INFORMATION *pkvpi)
{
    ULONG ulSize;
    NTSTATUS ntStatus;
    PKEY_VALUE_PARTIAL_INFORMATION pvpi;
    OBJECT_ATTRIBUTES objectAttributes;
    HANDLE hRegister;
    UNICODE_STRING usKeyName;
    UNICODE_STRING usValueName;

    RtlInitUnicodeString(&usKeyName, KeyName);
    RtlInitUnicodeString(&usValueName, ValueName);

    // 初始化
    InitializeObjectAttributes(&objectAttributes, &usKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 打开注册表Key
    ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (!NT_SUCCESS(ntStatus))
    {
        return FALSE;
    }

    // 查询长度
    ntStatus = ZwQueryValueKey(hRegister, &usValueName, KeyValuePartialInformation, NULL, 0, &ulSize);
    if (ntStatus == STATUS_OBJECT_NAME_NOT_FOUND || ulSize == 0)
    {
        return FALSE;
    }

    // 分配空间保存查询结果
    pvpi = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(PagedPool, ulSize);
    ntStatus = ZwQueryValueKey(hRegister, &usValueName, KeyValuePartialInformation, pvpi, ulSize, &ulSize);
    if (!NT_SUCCESS(ntStatus))
    {
        return FALSE;
    }

    // 这里的pvpi未被释放,可在外部释放
    // 执行 ExFreePool(pvpi); 释放
    *pkvpi = pvpi;
    return TRUE;
}

// 查询注册表键值
BOOLEAN MyQueryRegistryKeyValue(UNICODE_STRING ustrRegistry, UNICODE_STRING ustrKeyValueName)
{
    HANDLE hRegister = NULL;
    OBJECT_ATTRIBUTES objectAttributes = { 0 };
    NTSTATUS status = STATUS_SUCCESS;
    ULONG ulBufferSize = 0;
    PKEY_VALUE_PARTIAL_INFORMATION pKeyValuePartialInfo = NULL;

    // 初始化
    InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 打开注册表Key
    status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (!NT_SUCCESS(status))
    {
        return FALSE;
    }
    // 先获取查询注册表键值所需缓冲区的大小
    status = ZwQueryValueKey(hRegister, &ustrKeyValueName, KeyValuePartialInformation, NULL, 0, &ulBufferSize);
    if (0 == ulBufferSize)
    {
        ZwClose(hRegister);
        return FALSE;
    }

    // 申请缓冲区
    pKeyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(NonPagedPool, ulBufferSize);

    // 查询注册表键值并获取查询结果
    status = ZwQueryValueKey(hRegister, &ustrKeyValueName, KeyValuePartialInformation, pKeyValuePartialInfo, ulBufferSize, &ulBufferSize);
    if (!NT_SUCCESS(status))
    {
        ExFreePool(pKeyValuePartialInfo);
        ZwClose(hRegister);
        return FALSE;
    }
    // 显示查询结果
    DbgPrint("KeyValueName=%wZ, KeyValueType=%d, KeyValueData=%S\n", &ustrKeyValueName, pKeyValuePartialInfo->Type, pKeyValuePartialInfo->Data);

    // 释放内存, 关闭句柄
    ExFreePool(pKeyValuePartialInfo);
    ZwClose(hRegister);
    return TRUE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello lyshark \n");

    BOOLEAN flag = FALSE;
    DWORD get_dw = 0;

    PKEY_VALUE_PARTIAL_INFORMATION pkvi;

    // 查询设置
    flag = RegQueryValueKey(L"\\Registry\\Machine\\Software\\LySharkKeysA", L"is_auth", &pkvi);
    if (flag == TRUE)
    {
        // 拷贝查询结果
        RtlCopyMemory(&get_dw, pkvi->Data, pkvi->DataLength);

        // 输出结果
        DbgPrint("[*] 查询结果: %d \n", get_dw);
        ExFreePool(pkvi);
    }

    // 第二种查询方式
    UNICODE_STRING ustrRegistry;
    UNICODE_STRING ustrKeyValueName;

    RtlInitUnicodeString(&ustrRegistry, L"\\Registry\\Machine\\Software\\LySharkKeysA");
    RtlInitUnicodeString(&ustrKeyValueName, L"is_auth");

    MyQueryRegistryKeyValue(ustrRegistry, ustrKeyValueName);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行这段程序,将会查询\\Registry\\Machine\\Software\\LySharkKeysA下面的is_auth字段中的值,输出效果如下图所示;

8.5.5 ZwEnumerateKey

枚举某个主键底部的子键值,实现对指定主键中所有的子键的枚举。

ZwEnumerateKey是Windows内核中的一个函数,用于列举指定注册表键下的子键。它通常被驱动程序使用来获取键列表,以及子键的数量和名称等信息。

以下是ZwEnumerateKey函数的一般形式:

NTSTATUS ZwEnumerateKey(
  _In_ HANDLE                KeyHandle,
  _In_ ULONG                 Index,
  _In_ KEY_INFORMATION_CLASS KeyInformationClass,
  _Out_ PVOID                KeyInformation,
  _In_ ULONG                 Length,
  _Out_ PULONG               ResultLength
);

参数说明:

  • KeyHandle: 要列举子键的键的句柄(handle)。
  • Index: 指定要列举的子键的索引。
  • KeyInformationClass: 指定要获取的子键信息类型。
  • KeyInformation: 存储读取的子键信息的缓冲区。
  • Length: KeyInformation缓冲区的大小。
  • ResultLength: 实际读取的子键信息的大小。

函数执行成功时,将返回STATUS_SUCCESS,否则返回相应的错误代码。需要注意的是,在使用ZwEnumerateKey函数之前,需要先打开要列举子键的键,获取其句柄。

在使用ZwEnumerateKey函数时,需要注意权限和安全性问题,以避免潜在的安全问题。同时,需要仔细考虑键的名称和路径等信息,确保要列举子键的键是正确的,并且不会对系统造成不良影响。另外,需要确保KeyInformation缓冲区的大小足够,以存储读取的子键信息。

需要注意的是,ZwEnumerateKey函数只能用于列举单个键下的子键,如果需要列举多个键的子键,则需要自行实现循环操作。

#include <ntifs.h>
#include <windef.h>

// 枚举子键
BOOLEAN EnumRegistrySubKey(WCHAR *MY_KEY_NAME)
{
    UNICODE_STRING RegUnicodeString;
    HANDLE hRegister;
    OBJECT_ATTRIBUTES objectAttributes;
    NTSTATUS ntStatus;
    ULONG ulSize, i;
    UNICODE_STRING uniKeyName;
    PKEY_FULL_INFORMATION pfi;

    // 初始化UNICODE_STRING字符串
    RtlInitUnicodeString(&RegUnicodeString, MY_KEY_NAME);

    // 初始化objectAttributes OBJ_CASE_INSENSITIVE(大小写敏感)
    InitializeObjectAttributes(&objectAttributes, &RegUnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 打开注册表
    ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (!NT_SUCCESS(ntStatus))
    {
        return FALSE;
    }

    // 第一次调用获取KEY_FULL_INFORMATION数据的长度
    ZwQueryKey(hRegister, KeyFullInformation, NULL, 0, &ulSize);
    pfi = (PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, ulSize);

    // 第二次调用获取KEY_FULL_INFORMATION数据的数据
    ZwQueryKey(hRegister, KeyFullInformation, pfi, ulSize, &ulSize);

    // 循环输出子键
    for (i = 0; i<pfi->SubKeys; i++)
    {
        PKEY_BASIC_INFORMATION pbi;

        // 第一次调用获取KEY_BASIC_INFORMATION数据的长度
        ZwEnumerateKey(hRegister, i, KeyBasicInformation, NULL, 0, &ulSize);
        pbi = (PKEY_BASIC_INFORMATION)ExAllocatePool(PagedPool, ulSize);

        // 第二次调用获取KEY_BASIC_INFORMATION数据的数据
        ZwEnumerateKey(hRegister, i, KeyBasicInformation, pbi, ulSize, &ulSize);

        uniKeyName.Length = (USHORT)pbi->NameLength;
        uniKeyName.MaximumLength = (USHORT)pbi->NameLength;
        uniKeyName.Buffer = pbi->Name;

        DbgPrint("[LyShark] 序号: %d | 子Key名: %wZ \n", i, &uniKeyName);
        ExFreePool(pbi);
    }
    ExFreePool(pfi);
    ZwClose(hRegister);
    return TRUE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello lyshark \n");

    WCHAR MY_KEY_NAME[] = L"\\Registry\\Machine\\Software";
    BOOLEAN flag = EnumRegistrySubKey(MY_KEY_NAME);

    if (flag == TRUE)
    {
        DbgPrint("[*] 枚举结束 \n");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行如上代码片段,则会枚举\\Registry\\Machine\\Software底部的所有子键值,输出效果图如下所示;

8.5.6 ZwEnumerateValueKey

用于枚举子键下所有键值对的值,原理与上方枚举子键类似。

ZwEnumerateValueKey是Windows内核中的一个函数,用于列举指定注册表键下的所有值。它通常被驱动程序使用来获取键值列表,以及每个键值的名称、类型和数据等信息。

以下是ZwEnumerateValueKey函数的一般形式:

NTSTATUS ZwEnumerateValueKey(
  _In_ HANDLE                KeyHandle,
  _In_ ULONG                 Index,
  _In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
  _Out_ PVOID                KeyValueInformation,
  _In_ ULONG                 Length,
  _Out_ PULONG               ResultLength
);

参数说明:

  • KeyHandle: 要列举值的键的句柄(handle)。
  • Index: 指定要列举的值的索引。
  • KeyValueInformationClass: 指定要获取的值信息类型。
  • KeyValueInformation: 存储读取的值信息的缓冲区。
  • Length: KeyValueInformation缓冲区的大小。
  • ResultLength: 实际读取的值信息的大小。

函数执行成功时,将返回STATUS_SUCCESS,否则返回相应的错误代码。需要注意的是,在使用ZwEnumerateValueKey函数之前,需要先打开要列举值的键,获取其句柄。

在使用ZwEnumerateValueKey函数时,需要注意权限和安全性问题,以避免潜在的安全问题。同时,需要仔细考虑键的名称和路径等信息,确保要列举值的键是正确的,并且不会对系统造成不良影响。另外,需要确保KeyValueInformation缓冲区的大小足够,以存储读取的值信息。

需要注意的是,ZwEnumerateValueKey函数只能用于列举单个键下的所有值,如果需要列举多个键的所有值,则需要自行实现循环操作。

#include <ntifs.h>
#include <windef.h>

// 枚举子键
BOOLEAN EnumegistrySubValue(WCHAR *MY_KEY_NAME)
{
    UNICODE_STRING RegUnicodeString;
    HANDLE hRegister;
    OBJECT_ATTRIBUTES objectAttributes;
    ULONG ulSize, i;
    UNICODE_STRING uniKeyName;
    PKEY_FULL_INFORMATION pfi;
    NTSTATUS ntStatus;

    // 初始化UNICODE_STRING字符串
    RtlInitUnicodeString(&RegUnicodeString, MY_KEY_NAME);

    // 初始化objectAttributes
    InitializeObjectAttributes(&objectAttributes, &RegUnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 打开注册表
    ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (!NT_SUCCESS(ntStatus))
    {
        return FALSE;
    }

    // 查询VALUE的大小
    ZwQueryKey(hRegister, KeyFullInformation, NULL, 0, &ulSize);
    pfi = (PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, ulSize);
    ZwQueryKey(hRegister, KeyFullInformation, pfi, ulSize, &ulSize);
    for (i = 0; i<pfi->Values; i++)
    {
        PKEY_VALUE_BASIC_INFORMATION pvbi;

        // 查询单个VALUE的大小
        ZwEnumerateValueKey(hRegister, i, KeyValueBasicInformation, NULL, 0, &ulSize);
        pvbi = (PKEY_VALUE_BASIC_INFORMATION)ExAllocatePool(PagedPool, ulSize);

        // 查询单个VALUE的详情
        ZwEnumerateValueKey(hRegister, i, KeyValueBasicInformation, pvbi, ulSize, &ulSize);
        uniKeyName.Length = (USHORT)pvbi->NameLength;
        uniKeyName.MaximumLength = (USHORT)pvbi->NameLength;
        uniKeyName.Buffer = pvbi->Name;

        DbgPrint("[*] 子键: %d | 名称: %wZ | ", i, &uniKeyName);
        if (pvbi->Type == REG_SZ)
        {
            DbgPrint("类型: REG_SZ \n");
        }
        else if (pvbi->Type == REG_MULTI_SZ)
        {
            DbgPrint("类型: REG_MULTI_SZ \n");
        }
        else if (pvbi->Type == REG_DWORD)
        {
            DbgPrint("类型: REG_DWORD \n");
        }
        else if (pvbi->Type == REG_BINARY)
        {
            DbgPrint("类型: REG_BINARY \n");
        }
        ExFreePool(pvbi);
    }

    ExFreePool(pfi);
    ZwClose(hRegister);
    return TRUE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello lyshark \n");

    WCHAR MY_KEY_NAME[] = L"\\Registry\\Machine\\Software\\LySharkKeysA";
    BOOLEAN flag = EnumegistrySubValue(MY_KEY_NAME);

    if (flag == TRUE)
    {
        DbgPrint("[*] 枚举结束 \n");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行如上这段代码,则可枚举出\\Registry\\Machine\\Software\\LySharkKeysA底部的所有子键以及该子键的键值,输出效果如下图所示;

8.5.7 ZwDeleteValueKey

用于删除指定键里面键值对的某个值。如果使用函数RegDeleteKey则删除键包括里面的所有值。

ZwDeleteValueKey是Windows内核中的一个函数,用于删除指定注册表键下的一个值。它通常被驱动程序使用来删除指定键下的一个值,以及释放该值占用的空间。

以下是ZwDeleteValueKey函数的一般形式:

NTSTATUS ZwDeleteValueKey(
  _In_ HANDLE           KeyHandle,
  _In_ PUNICODE_STRING ValueName
);

参数说明:

  • KeyHandle: 要删除值的键的句柄(handle)。
  • ValueName: 要删除的值的名称,为Unicode字符串指针。

函数执行成功时,将返回STATUS_SUCCESS,否则返回相应的错误代码。需要注意的是,在使用ZwDeleteValueKey函数之前,需要先打开要删除值的键,获取其句柄。

在使用ZwDeleteValueKey函数时,需要注意权限和安全性问题,以避免潜在的安全问题。同时,需要仔细考虑键的名称和路径等信息,确保要删除的值是正确的,并且不会对系统造成不良影响。

需要注意的是,ZwDeleteValueKey函数只能用于删除单个键下的一个值,如果需要删除多个键的多个值,则需要自行实现循环操作。

#include <ntifs.h>
#include <windef.h>

// 删除键中的值
BOOLEAN RegDeleteValueKey(LPWSTR KeyName, LPWSTR ValueName)
{
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING usKeyName, usValueName;
    NTSTATUS ntStatus;
    HANDLE hRegister;
    RtlInitUnicodeString(&usKeyName, KeyName);
    RtlInitUnicodeString(&usValueName, ValueName);

    InitializeObjectAttributes(&objectAttributes, &usKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 打开注册表
    ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (NT_SUCCESS(ntStatus))
    {
        ntStatus = ZwDeleteValueKey(hRegister, &usValueName);
        ZwFlushKey(hRegister);
        ZwClose(hRegister);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
    return FALSE;
}

// 删除注册表键值
BOOLEAN MyDeleteRegistryKeyValue(UNICODE_STRING ustrRegistry, UNICODE_STRING ustrKeyValueName)
{
    HANDLE hRegister = NULL;
    OBJECT_ATTRIBUTES objectAttributes = { 0 };
    NTSTATUS status = STATUS_SUCCESS;
    // 打开注册表键
    InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL);
    status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (!NT_SUCCESS(status))
    {
        return FALSE;
    }

    // 删除注册表键
    status = ZwDeleteValueKey(hRegister, &ustrKeyValueName);
    if (!NT_SUCCESS(status))
    {
        ZwClose(hRegister);
        return FALSE;
    }

    // 关闭注册表键句柄
    ZwClose(hRegister);
    return TRUE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello lyshark \n");

    // 删除值
    BOOLEAN flag = RegDeleteValueKey(L"\\Registry\\Machine\\Software\\LySharkKeysA", L"is_auth");

    if (flag == TRUE)
    {
        DbgPrint("[*] 删除子键 \n");
    }

    UNICODE_STRING ustrRegistry;
    UNICODE_STRING ustrKeyValueName;

    RtlInitUnicodeString(&ustrRegistry, L"\\Registry\\Machine\\Software\\LySharkKeysA");
    RtlInitUnicodeString(&ustrKeyValueName, L"is_trhe");
    flag = MyDeleteRegistryKeyValue(ustrRegistry, ustrKeyValueName);
    if (flag == TRUE)
    {
        DbgPrint("[*] 删除子键 \n");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行如上驱动程序,则会将\\Registry\\Machine\\Software\\LySharkKeysA里面的is_trhe以及is_auth删除,效果图如下所示;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/165374.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

接口自动化测试很难吗?来看看这份超详细的教程!

接口自动化测试框架目的 测试工程师应用自动化测试框架的目的: 增强测试脚本的可维护性、易用性(降低公司自动化培训成本&#xff0c;让公司的测试工程师都可以开展自动化测试)。 以下框架以微信公众平台开放文档实战 地址&#xff1a;https://developers.weixin.qq.com/doc…

车载通信架构 —— 传统车内通信网络发展回顾

车载通信架构 —— 传统车内通信网络发展回顾 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何…

CD36 ; + Lectin;

CD2 LIMP-2&#xff0c; LGP85 SR-BI&#xff0c; CD36&#xff1b; 清道夫受体蛋白CD36超家族的成员是 脂质代谢 和 先天免疫 的重要调节因子。它们识别正常和修饰的脂蛋白&#xff0c;以及与病原体相关的分子模式。 该家族由三个成员组成&#xff1a; SR-BI &am…

什么是tomcat, tomcat该如何使用?(java)

tomcat是什么? tomcat翻译过来为汤姆猫, 但是他可不是猫和老鼠中的汤姆, 而是java中的tom, 虽然java中的tomcat没有猫和老鼠那么出名, 但是他仍然是java中的中流砥柱 下图为java中的tomcat, 也就是最右边这个黄色的猫: Tomcat是Apache 软件基金会&#xff08;Apache Software …

2024年软件测试面试必看系列,看完去面试你会感谢我的!!

朋友圈点赞的测试用例 功能测试 1点赞后是否显示结果 2.点赞后是否可以取消; 3.点赞取消后是否可以重复点赞; 4.共同好友点赞后&#xff0c;是否有消息提醒; 5.非共同好友点赞后&#xff0c;是否有消息提醒; 6.点击点赞人昵称&#xff0c;是否可以跳转到他/她的主页; 7.自己能…

图像倾斜角度求取-Radon变换

Radon算法 Radon&#xff08;拉东&#xff09;算法是一种通过定方向投影叠加&#xff0c;找到最大投影值时角度&#xff0c;从而确定图像倾斜角度的算法。具体过程如图所示 图1 Radon变换算法 Radon计算示例 对于纹理方向明显的图像&#xff0c;如图2所示&#xff0c;可以通…

基于springboot实现应急救援物资管理系统项目【项目源码】计算机毕业设计

基于springboot实现应急救援物资管理系统演示 JAVA简介 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&a…

数学建模-图与网络模型解题方法和代码实现

本文针对以下几个方面问题进行整理&#xff1a; 最短路问题 两个指定顶点之间的最短路径任意顶点之间的最短路径 2.最小生成树问题 求最小生成树 3.网络最大流问题 源点与汇点之间的最大流基于最大流的最小费用求解 4.旅行商问题 基于哈密顿(Hamilton)圈求解旅行商线性…

九、Linux用户管理

1.基本介绍 Linux系统是一个多用户多任务的操作系统&#xff0c;任何一个要使用系统资源的用户&#xff0c;都必须首先向系统管理员申请一个账号&#xff0c;让后以这个账号的身份进入系统 2.添加用户 基本语法 useradd 用户名 应用案例 案例1&#xff1a;添加一个用户 m…

[游戏开发][Untiy]跨平台可视化Log系统

工具介绍 今天介绍的主角是LogViewer 工具运行时长这个样子&#xff0c;Unity的Log日志都会在这里显示 如何安装 在Unity商店搜索Log&#xff0c;排名第一的就是它 也可以去Github官网下载源码&#xff1a; Unity-Logs-Viewerhttps://github.com/aliessmael/Unity-Logs-Vie…

六.Linux远程登录

1.说明&#xff1a;公司开发的时候&#xff0c;具体的应用场景是这样的 1.linux服务器是开发小组共享 2.正式上线的项目是运行在公网 3.因此程序员需要远程登录到Linux进行项目管理或者开发 4.画出简单的网络拓扑示意图(帮助理解) 5.远程登录客户端有Xshell6、Xftp6&#xff0…

星火模型(Spark)的langchain 实现

星火模型的langchain实现 测试已通过&#xff0c;希望有所帮助。 使用前请先安装环境&#xff1a; pip install githttps://github.com/shell-nlp/spark-ai-python.git注意&#xff1a; 一定要使用上面方式安装spark库&#xff0c;因对官方的库做了改动。官方的库已经长时间不…

基于RK3588全高端智能终端机器人主板

一、小尺寸板型设计 该款主板为小型板&#xff0c;尺寸仅为125*85mm&#xff0c;更小更紧凑&#xff0c;可完美适应各类高端智能自助终端&#xff1b; 二、八核高端处理器 采用RK3588S八核64位处理器&#xff0c;8nm LP制程&#xff0c;主频最高达2.4GHz&#xff0c;搭载Andr…

吾爱破解置顶的“太极”,太好用了吧!

日常工作和娱乐&#xff0c;都需要用到不同类型的软件&#xff0c;哪怕软件体积不大&#xff0c;也必须安装&#xff0c;否则到用时找不到就非常麻烦了。 其实&#xff0c;很多软件不一定一样不剩地全部安装一遍&#xff0c;一方面原因是用的不多&#xff0c;另一方面多少有点…

95. 最长公共子序列

题目 题解 class Solution:def longestCommonSubsequence(self, text1: str, text2: str) -> int:# 定义状态&#xff1a;dp[i][j]表示s1[0:i]和s2[0:j]的最长公共子序列dp [[0 for j in range(len(text2)1)] for i in range(len(text1) 1)]# badcase: dp[i][0] 0, dp[0…

Python操作Excel常用方法汇总

目录 引言 一、使用pandas库操作Excel 1、读取Excel文件 2、写入Excel文件 3、处理Excel数据 二、使用openpyxl库操作Excel 1、读取Excel文件 2、写入Excel文件 3、处理Excel数据 三、高级功能 总结 引言 Python是一种功能强大的编程语言&#xff0c;它可以用来处理…

概念解析 | 网络安全数字孪生(Digital Twin of Cyber Security, DTCS)技术

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:网络安全数字孪生。 概念解析 | 网络安全的“数字镜像” —— 网络安全数字孪生 1. 背景介绍 随着数字化转型进程的深入推进,网络空间安全问题日益凸显。当前的网络安全防护面…

【win32_000】视频截图

PPT 编译器不会自己添加unicode定义 v 函数 WinMain int __clrcall WinMain([in] HINSTANCE hInstance ,//应用程序的当前实例的句柄。[in, optional] HINSTANCE hPrevInstance ,//应用程序上一个实例的句柄。 此参数始终为 NULL。[in] …

【坑】从源码安装Nav2(ROS2-iron) (不兼容的ompl和nav2)

文章目录 前言三种安装方式应当具备的知识源码安装Nav2找到Nav2的仓库下载源码下依赖构建源码构建源码中遇到的问题找不到Config.cmakefatal error: Eigen/Core: No such file or directoryoom C: fatal error: Killed signal terminated program cc1pluserror: RPC failed&…

【Linux进程】进程等待 与 进程替换 原理与函数使用

文章目录 一、进程等待1.1 意义 / 必要性1.2 进程等待的函数&#xff08;wait / waitpid&#xff09;1.3 status参数1.4 获取子进程status1.5 进程的阻塞等待与非阻塞等待 二、进程替换2.1 引言2.2 进程替换原理2.3 替换函数 一、进程等待 1.1 意义 / 必要性 为什么要有进程等…