驅動開發:內核註冊表增刪改查

来源:https://www.cnblogs.com/LyShark/archive/2023/06/21/17158485.html
-Advertisement-
Play Games

註冊表是Windows中的一個重要的資料庫,用於存儲系統和應用程式的設置信息,註冊表是一個巨大的樹形結構,無論在應用層還是內核層操作註冊表都有獨立的API函數可以使用,而在內核中讀寫註冊表則需要使用內核裝用API函數,如下將依次介紹並封裝一些案例,實現對註冊表的創建,刪除,更新,查詢等操作。 ...


註冊表是Windows中的一個重要的資料庫,用於存儲系統和應用程式的設置信息,註冊表是一個巨大的樹形結構,無論在應用層還是內核層操作註冊表都有獨立的API函數可以使用,而在內核中讀寫註冊表則需要使用內核裝用API函數,如下將依次介紹並封裝一些案例,實現對註冊表的創建,刪除,更新,查詢等操作。

在Windows內核中,註冊表是一種存儲系統配置信息的機制,包括應用程式、硬體、驅動程式和操作系統的各種設置。內核提供了一些API函數,可以讓驅動程式通過代碼訪問和修改註冊表,以實現系統的配置和管理。下麵簡單介紹一下內核中的註冊表增刪改查操作:

註冊表查詢

  • 在內核中,可以使用ZwQueryValueKey或ZwEnumerateValueKey函數查詢指定鍵的值。其中,ZwQueryValueKey函數可以查詢指定鍵的值,而ZwEnumerateValueKey函數可以枚舉指定鍵下的所有值。這兩個函數都需要指定鍵的句柄和要查詢的值的名稱,查詢結果將返回在指定的緩衝區中。

註冊表修改

  • 在內核中,可以使用ZwSetValueKey函數修改指定鍵的值。該函數需要指定鍵的句柄、要修改的值的名稱、值的類型和值的數據。在修改註冊表時,需要註意許可權和安全性問題,以避免潛在的安全問題。

註冊表添加

  • 在內核中,可以使用ZwCreateKey函數創建一個新的鍵。該函數需要指定要創建鍵的父鍵的句柄、新鍵的名稱、新鍵的屬性等信息。如果成功創建了新鍵,則可以使用ZwSetValueKey函數向其添加值。

註冊表刪除

  • 在內核中,可以使用ZwDeleteValueKey函數刪除指定鍵的值,或使用ZwDeleteKey函數刪除指定鍵及其下麵的所有子鍵和值。這兩個函數都需要指定要刪除的鍵的句柄或路徑。在刪除註冊表時,同樣需要註意許可權和安全性問題,以避免潛在的安全問題。

需要註意的是,對註冊表的操作可能會對系統的穩定性產生影響。因此,在實現這些技術時,需要遵循操作系統和安全軟體的規定,以確保系統的安全和穩定。

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.com \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.com \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則會提示打開失敗,輸出效果如下所示;

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.com \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,輸出效果如下所示;

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.com \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分別創建一個整數,布爾值,字元串類型,效果圖如下;

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.com \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欄位中的值,輸出效果如下圖所示;

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.com \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底部的所有子鍵值,輸出效果圖如下所示;

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.com \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底部的所有子鍵以及該子鍵的鍵值,輸出效果如下圖所示;

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.com \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刪除,效果圖如下所示;

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/17158485.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 帶聚合函數的SQL邏輯,我們可以根據其執行過程的不同,將其分成三大類來進行分析: 僅在Reduce階段聚合的SQL執行邏輯 在Map和Reduce階段都有聚合操作的SQL執行邏輯 高級分組聚合的執行SQL邏輯 ...
  • Apache Hudi 是一款開源的[數據湖解決方案](https://www.dtstack.com/dtengine/easylake?src=szsm),它能夠幫助企業更好地管理和分析海量數據,支持高效的[數據更新和查詢](https://www.dtstack.com/dtengine/ea ...
  • [toc] >VersionedCollapsingMergeTree引擎繼承自MergeTree並將摺疊行的邏輯添加到合併數據部分的演算法中。VersionedCollapsingMergeTree用於相同的目的摺疊樹但使用不同的摺疊演算法,允許以多個線程的任何順序插入數據。特別是,Version列有 ...
  • 本文對Clickhouse架構原理、語法、性能特點做一定研究,同時將其與mysql、elasticsearch、tidb做橫向對比,並重點分析與mysql的語法差異,為有mysql遷移clickhouse場景需求的技術預研及參考。 ...
  • # 關聯文章: - [MySQL對derived table的優化處理與使用限制](http://mp.weixin.qq.com/s?__biz=MzkzMTIzMDgwMg==&mid=2247501528&idx=1&sn=03ea1cd0eb8f42bacda0583879ad494c&ch ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 本文介紹了一種新的HTML元素搜索方法,並提供了一個實用的工具來幫助開發者快速找到所需的元素。這對於那些需要處理大量HTML元素的開發者來說是非常有用的。文章還通過提供一些常見元素的用法示例,幫助開發者更好地理解和應用這些元素。在眾多元素 ...
  • 前不久,在網上看到這麼一張非常有趣的圖: ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4edc3cef70d744d381eabb604ef089cb~tplv-k3u1fbpfcp-watermark.image?) 想必很多同學都看 ...
  • Stencil 組件使用 `JSX` 渲染,這是一種流行的聲明式模板語法。每個組件都有一個渲染函數,它返回在運行時渲染到 DOM 的組件樹。 ## 基礎用法 `render` 函數用於輸出將繪製到屏幕上的組件樹。 ```ts class MyComponent { render() { return ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...