主頁 > 軟體設計 > 驅動開發:內核注冊表增刪改查

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

2023-06-22 08:06:01 軟體設計

注冊表是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=https://www.cnblogs.com/LyShark/p/%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 許可協議,轉載請注明出處!

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/555791.html

標籤:領域驅動設計

上一篇:讀發布!設計與部署穩定的分布式系統(第2版)筆記08_自黑與放大

下一篇:返回列表

標籤雲
其他(161476) Python(38244) JavaScript(25512) Java(18251) C(15238) 區塊鏈(8271) C#(7972) AI(7469) 爪哇(7425) MySQL(7260) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5875) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4606) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2436) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) HtmlCss(1971) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 驅動開發:內核注冊表增刪改查

    注冊表是Windows中的一個重要的資料庫,用于存盤系統和應用程式的設定資訊,注冊表是一個巨大的樹形結構,無論在應用層還是內核層操作注冊表都有獨立的API函式可以使用,而在內核中讀寫注冊表則需要使用內核裝用API函式,如下將依次介紹并封裝一些案例,實作對注冊表的創建,洗掉,更新,查詢等操作。 ......

    uj5u.com 2023-06-22 08:06:01 more
  • 讀發布!設計與部署穩定的分布式系統(第2版)筆記08_自黑與放大

    ![](https://img2023.cnblogs.com/blog/3076680/202306/3076680-20230621151546278-1606324122.png) # 1. 自黑式攻擊 ## 1.1. 自黑只會偶爾成為人類的美德 ## 1.2. 對系統來說,絕對不會推崇自黑 ......

    uj5u.com 2023-06-22 08:05:37 more
  • 工業互聯網專案開發作業流程及核心問題

    工業互聯網專案開發全流程V3.0 工業互聯網專案開發作業流程及核心問題 一、需求分析 1、共享平臺需求分析 這個平臺要解決什么問題?這個平臺的用戶群體是誰?這個平臺應該具備哪些主要功能?這個平臺的使用場景是什么?這個平臺如何與現有的系統集成? 2、需求檔案設計 需求檔案是否詳細、明確,且可以量化?是 ......

    uj5u.com 2023-06-21 08:49:53 more
  • 工程師常用的6種最佳實踐

    一、約定大于配置 泰思勒定律也被稱為復雜度守恒定律。該定律指出每一個程序都有其固有的復雜性,存在一個臨界點,超過了這個點程序就不能再簡化了,你只能將固有的復雜性從一個地方移動到另外一個地方。 根據這個定律,在做系統設計時,默認會給用戶一個“套餐”,這個套餐會滿足多數人的需求。實在不滿足需求再特殊配置 ......

    uj5u.com 2023-06-21 08:49:34 more
  • 工業互聯網專案開發作業流程及核心問題

    工業互聯網專案開發全流程V3.0 工業互聯網專案開發作業流程及核心問題 一、需求分析 1、共享平臺需求分析 這個平臺要解決什么問題?這個平臺的用戶群體是誰?這個平臺應該具備哪些主要功能?這個平臺的使用場景是什么?這個平臺如何與現有的系統集成? 2、需求檔案設計 需求檔案是否詳細、明確,且可以量化?是 ......

    uj5u.com 2023-06-21 08:48:49 more
  • 工程師常用的6種最佳實踐

    一、約定大于配置 泰思勒定律也被稱為復雜度守恒定律。該定律指出每一個程序都有其固有的復雜性,存在一個臨界點,超過了這個點程序就不能再簡化了,你只能將固有的復雜性從一個地方移動到另外一個地方。 根據這個定律,在做系統設計時,默認會給用戶一個“套餐”,這個套餐會滿足多數人的需求。實在不滿足需求再特殊配置 ......

    uj5u.com 2023-06-21 08:48:30 more
  • Java設計模式之代理模式--經紀人的作業

    代理模式是23種設計模式中結構型模式的一種,它的核心是通過代理類來完成其他物件的訪問,降低訪問者和被訪問者的耦合度,也對功能進行了增強。 ......

    uj5u.com 2023-06-20 10:32:31 more
  • 如何設計一個海量任務調度系統

    在日常開發中會經常遇到一些需要異步定時執行的業務訴求,典型的使用場景如:超時未支付訂單關單、每隔 2h 更新好友排行榜、3.22 日 17 點《xx》劇上線等。目前業務側多基于以下思路來快速搭建一個調度系統,mysql 或者 redis 佇列存盤待執行任務,通過 crontab 定時觸發應用完成“撈... ......

    uj5u.com 2023-06-20 10:26:58 more
  • Java設計模式之代理模式--經紀人的作業

    代理模式是23種設計模式中結構型模式的一種,它的核心是通過代理類來完成其他物件的訪問,降低訪問者和被訪問者的耦合度,也對功能進行了增強。 ......

    uj5u.com 2023-06-20 10:16:34 more
  • 如何設計一個海量任務調度系統

    在日常開發中會經常遇到一些需要異步定時執行的業務訴求,典型的使用場景如:超時未支付訂單關單、每隔 2h 更新好友排行榜、3.22 日 17 點《xx》劇上線等。目前業務側多基于以下思路來快速搭建一個調度系統,mysql 或者 redis 佇列存盤待執行任務,通過 crontab 定時觸發應用完成“撈... ......

    uj5u.com 2023-06-20 09:27:45 more