《Windows驱动开发技术详解》之Windows内核函数
- 内核模式下字符串操作
ANSI_STRING和UNICODE_STRING分别定义如下:
以UnicodeString类型对象进行初始化为例,代码如下:
输出:
进行复制字符串操作,代码如下:
输出:
但是如果这里改为:
加载驱动运行就会蓝屏。Why?其实,RltFreeUnicodeString是用来释放利用申请的堆空间初始化的UnicodeString类型对象的,而RtlInitUnicodeString对UniStr1进行初始化时,只是让Buffer指向了一个常量区。
进行ANSI_STRING字符串与UNICODE_STRING字符串相互转换操作,代码如下:
注意这里要利用RtlFreeUnicodeString释放通过RtlAnsiStringToUnicodeString得到的UniStr2。为什么这个需要释放?我们利用Windbg跟踪下代码。
首先,跟踪时要逐一,这里的勾如果不去掉,就是在源码下单步跟踪,而不是在汇编指令里单步跟踪:
在RtlUnicodeStringToAnsiString函数中,有这么一个系统API
此时的参数是
正好是传入的字节数的大小。而这个API最终调用了:
所以,我们要利用RtlFreeUnicodeString进行释放。
- 内核模式下的文件操作:
创建文件:
代码入下:
1 VOID FILEOPERATION(){ 2 OBJECT_ATTRIBUTES ObjAttributes; 3 IO_STATUS_BLOCK iostatus; 4 HANDLE hfile; 5 UNICODE_STRING logFileUnicodeString; 6 7 RtlInitUnicodeString(&logFileUnicodeString, L"\\??\\C:\\1.log"); 8 InitializeObjectAttributes(&ObjAttributes, 9 &logFileUnicodeString, 10 OBJ_CASE_INSENSITIVE, 11 NULL, 12 NULL); 13 //创建文件 14 NTSTATUS status = ZwCreateFile(&hfile, GENERIC_WRITE, 15 &ObjAttributes, 16 &iostatus, 17 NULL, 18 FILE_ATTRIBUTE_NORMAL, 19 FILE_SHARE_READ, 20 FILE_OPEN_IF,//这里是FILE_OPEN_IF则不论文件是否存在都可以Create成功,而如果改为FILE_OPEN,则只当文件存在时create成功。 21 FILE_SYNCHRONOUS_IO_NONALERT, 22 NULL, 23 0); 24 if (NT_SUCCESS(status)){ 25 DbgPrint("Create file succeed!\n"); 26 } 27 else{ 28 DbgPrint("Create file failed!\n"); 29 } 30 ZwClose(hfile); 31 }
如图
把“1.log”文件删除后,改为OPEN_FILE标志,则会失败:
除了使用ZwCreateFile加上标志FILE_OPEN之外,还可以使用ZwOpenFile来直接打开文件。只需将ZwCreateFile改为ZwOpenFile即可:
获取或修改文件的属性,其代码如下:
你要去查询、设置一个文件不同的属性,那么就要定义不同的FileInformationClass。
文件写操作,其代码如下:
1 VOID WRITEREADFILE(){ 2 OBJECT_ATTRIBUTES ObjAttributes; 3 IO_STATUS_BLOCK iostatus; 4 HANDLE hfile; 5 UNICODE_STRING logFileUnicodeString; 6 7 RtlInitUnicodeString(&logFileUnicodeString, L"\\??\\C:\\1.log"); 8 InitializeObjectAttributes(&ObjAttributes, 9 &logFileUnicodeString, 10 OBJ_CASE_INSENSITIVE, 11 NULL, 12 NULL); 13 //创建文件 14 NTSTATUS status = ZwCreateFile(&hfile, GENERIC_WRITE, 15 &ObjAttributes, 16 &iostatus, 17 NULL, 18 FILE_ATTRIBUTE_NORMAL, 19 FILE_SHARE_READ, 20 FILE_OPEN_IF, 21 FILE_SYNCHRONOUS_IO_NONALERT, 22 NULL, 23 0); 24 25 if (NT_SUCCESS(status)){ 26 DbgPrint("Create file succeed!\n"); 27 } 28 else{ 29 DbgPrint("Create file failed!\n"); 30 } 31 //构造要填充的数据 32 PUCHAR pBuffer = (PUCHAR)ExAllocatePool(PagedPool, BUFFER_SIZE); 33 RtlFillMemory(pBuffer, BUFFER_SIZE, 0x65); 34 //写文件 35 ZwWriteFile(hfile, NULL, NULL, NULL, &iostatus, pBuffer, BUFFER_SIZE, NULL, NULL); 36 DbgPrint("Write file 0x65"); 37 38 //构造要填充的数据 39 RtlFillMemory(pBuffer, BUFFER_SIZE, 0xBB); 40 LARGE_INTEGER number; 41 number.QuadPart = 1024i64;//设置文件指针,再次写入文件 42 ZwWriteFile(hfile, NULL, NULL, NULL, &iostatus, pBuffer, BUFFER_SIZE, &number, NULL); 43 DbgPrint("Write file 0xBB"); 44 ZwClose(hfile); 45 ExFreePool(pBuffer); 46 }
运行后可以看到文件中被写入数据:
注意这个API:
如果这里填充了大于已经开辟了的堆空间的数据,如改为2*BUFFER_SIZE,则会造成蓝屏:
说明这个API是会覆盖一些正常的数据的。
文件读操作,代码如下:
1 VOID ReadFileTest(){ 2 OBJECT_ATTRIBUTES ObjAttributes; 3 IO_STATUS_BLOCK iostatus; 4 HANDLE hfile; 5 UNICODE_STRING logFileUnicodeString; 6 7 RtlInitUnicodeString(&logFileUnicodeString, L"\\??\\C:\\1.log"); 8 InitializeObjectAttributes(&ObjAttributes, 9 &logFileUnicodeString, 10 OBJ_CASE_INSENSITIVE, 11 NULL, 12 NULL); 13 //创建文件 14 NTSTATUS status = ZwCreateFile(&hfile, GENERIC_WRITE, 15 &ObjAttributes, 16 &iostatus, 17 NULL, 18 FILE_ATTRIBUTE_NORMAL, 19 FILE_SHARE_READ, 20 FILE_OPEN_IF, 21 FILE_SYNCHRONOUS_IO_NONALERT, 22 NULL, 23 0); 24 25 if (NT_SUCCESS(status)){ 26 DbgPrint("Create file succeed!\n"); 27 } 28 else{ 29 DbgPrint("Create file failed!\n"); 30 } 31 FILE_STANDARD_INFORMATION fsi; 32 status = ZwQueryInformationFile(hfile, &iostatus, &fsi, 33 sizeof(FILE_STANDARD_INFORMATION), 34 FileStandardInformation); 35 PUCHAR pBuffer = (PUCHAR)ExAllocatePool(PagedPool, (LONG)fsi.EndOfFile.QuadPart); 36 37 ZwReadFile(hfile, NULL, NULL, NULL, &iostatus, pBuffer, 38 (LONG)fsi.EndOfFile.QuadPart, 39 NULL, NULL); 40 DbgPrint("Read %d bytes\n", iostatus.Information); 41 DbgPrint("Content:%s\n", pBuffer); 42 ZwClose(hfile); 43 ExFreePool(pBuffer); 44 }
输出结果:
- 内核模式下的注册表操作:
1 //新建注册表项为HKEY_LOCAL_MACHINE\SOFTWARE\HELLODDK 2 #define MY_REG_SOFTWARE_KEY_NAME L"\\Registry\\Machine\\Software\\HELLODDK" 3 4 VOID CreateRegisterTest(){ 5 UNICODE_STRING RegUnicodeString; 6 HANDLE hRegister; 7 RtlInitUnicodeString(&RegUnicodeString, MY_REG_SOFTWARE_KEY_NAME); 8 OBJECT_ATTRIBUTES objectAttributes; 9 InitializeObjectAttributes(&objectAttributes, 10 &RegUnicodeString, 11 OBJ_CASE_INSENSITIVE, 12 NULL, NULL); 13 ULONG ulResult; 14 //创建或打开注册表项目 15 NTSTATUS status = ZwCreateKey(&hRegister, KEY_ALL_ACCESS, 16 &objectAttributes, 17 0, NULL, 18 REG_OPTION_NON_VOLATILE, 19 &ulResult); 20 if (NT_SUCCESS(status)){ 21 if (ulResult == REG_CREATED_NEW_KEY){ 22 DbgPrint("Register item is created!\n"); 23 } 24 else if(ulResult == REG_OPENED_EXISTING_KEY){ 25 DbgPrint("Register item was created!\n"); 26 } 27 } 28 //***************************************************// 29 //创建或打开某注册表项的子项 30 UNICODE_STRING subRegUnicodeString; 31 HANDLE hSubRegister; 32 RtlInitUnicodeString(&subRegUnicodeString, L"SubItem"); 33 OBJECT_ATTRIBUTES subObjectAttributes; 34 InitializeObjectAttributes(&subObjectAttributes, 35 &subRegUnicodeString, 36 OBJ_CASE_INSENSITIVE, 37 hRegister, NULL); 38 //对于InitializeObjectAttributes的倒数第二个参数 39 //与ObjectName参数匹配的根目录对象,如果ObjectName是对象的全路径则设置此参数为NULL, 40 //使用ZwCreateDirectoryObject 获取一个目录对象。 41 status = ZwCreateKey(&hSubRegister, 42 KEY_ALL_ACCESS, 43 &subObjectAttributes, 44 0, NULL, 45 REG_OPTION_NON_VOLATILE, 46 &ulResult); 47 if (NT_SUCCESS(status)){ 48 if (ulResult == REG_CREATED_NEW_KEY){ 49 DbgPrint("Subregister item is created!\n"); 50 } 51 else if (ulResult == REG_OPENED_EXISTING_KEY){ 52 DbgPrint("Subregister item was created!\n"); 53 } 54 } 55 ZwClose(hRegister); 56 ZwClose(hSubRegister); 57 }
会分别创建表项和子项:
用ZwCreateKey可以打开注册表,同样用ZwOpenKey也可以。只是ZwOpenKey在当没有这个表项的时候不会去创建,而是返回一个错误。
添加、修改注册表键值,代码如下:
1 VOID SetRegisterTest(){ 2 UNICODE_STRING regUnicodeString; 3 HANDLE hRegister; 4 RtlInitUnicodeString(®UnicodeString, 5 MY_REG_SOFTWARE_KEY_NAME); 6 OBJECT_ATTRIBUTES objectAttributes; 7 InitializeObjectAttributes(&objectAttributes, 8 ®UnicodeString, 9 OBJ_CASE_INSENSITIVE, 10 NULL, NULL); 11 NTSTATUS status = ZwOpenKey(&hRegister, 12 KEY_ALL_ACCESS, 13 &objectAttributes); 14 if (NT_SUCCESS(status)){ 15 DbgPrint("Add value!\n"); 16 } 17 18 //设置一个键名、键值、键类型 19 //****************************************************************************** 20 UNICODE_STRING ValueName; 21 //设置键名 22 RtlInitUnicodeString(&ValueName, 23 L"DwordValue"); 24 ULONG ulValue = 1000;//设置键值 25 ZwSetValueKey(hRegister, &ValueName, 0, REG_DWORD, &ulValue, sizeof(ulValue)); 26 //****************************************************************************** 27 28 29 RtlInitUnicodeString(&ValueName, L"SZValue"); 30 WCHAR* strValue = L"hello world"; 31 ZwSetValueKey(hRegister, &ValueName, 0, REG_SZ, strValue, (ULONG)(wcslen(strValue) * 2 + 2)); 32 33 RtlInitUnicodeString(&ValueName, L"BianryValue"); 34 UCHAR Buffer[10]; 35 RtlFillMemory(Buffer, sizeof(Buffer), 0x73); 36 ZwSetValueKey(hRegister, &ValueName, 0, REG_BINARY, Buffer, sizeof(Buffer)); 37 38 ZwClose(hRegister); 39 }
运行输出结果如下:
查询注册表,代码如下:
1 VOID QueryRegisterTest(){ 2 UNICODE_STRING RegUnicodeString; 3 HANDLE hRegister; 4 RtlInitUnicodeString(&RegUnicodeString, MY_REG_SOFTWARE_KEY_NAME); 5 OBJECT_ATTRIBUTES objectAttributes; 6 InitializeObjectAttributes(&objectAttributes, 7 &RegUnicodeString, 8 OBJ_CASE_INSENSITIVE, 9 NULL, NULL); 10 NTSTATUS status = ZwOpenKey(&hRegister, 11 KEY_ALL_ACCESS, 12 &objectAttributes); 13 if (NT_SUCCESS(status)){ 14 DbgPrint("Open register succeed!\n"); 15 } 16 17 UNICODE_STRING ValueName; 18 RtlInitUnicodeString(&ValueName, L"DwordValue"); 19 ULONG ulSize; 20 //第一次查询,获得所要查询的数据的长度 21 status = ZwQueryValueKey(hRegister, 22 &ValueName, 23 KeyValuePartialInformation, 24 NULL, 0, &ulSize); 25 if (status == STATUS_OBJECT_NAME_NOT_FOUND || ulSize == 0){ 26 ZwClose(hRegister); 27 DbgPrint("Value name not found!\n"); 28 return; 29 } 30 PKEY_VALUE_PARTIAL_INFORMATION pvpi = (PKEY_VALUE_PARTIAL_INFORMATION) 31 ExAllocatePool(PagedPool, ulSize); 32 //开辟完堆空间之后,去获取内容 33 status = ZwQueryValueKey(hRegister, &ValueName, KeyValuePartialInformation, 34 pvpi, ulSize, &ulSize); 35 if (!NT_SUCCESS(status)){ 36 ZwClose(hRegister); 37 DbgPrint("Query failed!\n"); 38 return; 39 } 40 if (pvpi->Type == REG_DWORD&&pvpi->DataLength == sizeof(ULONG)){ 41 PULONG pulValue = (PULONG)pvpi->Data; 42 DbgPrint("Query value:%d!\n", *pulValue); 43 } 44 ExFreePool(pvpi); 45 ZwClose(hRegister); 46 }
运行结果如下,可以看到查询结果:
枚举子项,代码如下:
1 VOID EnumSubkeyTest(){ 2 UNICODE_STRING regUnicodeString; 3 HANDLE hRegister; 4 RtlInitUnicodeString(®UnicodeString, MY_REG_SOFTWARE_KEY_NAME); 5 OBJECT_ATTRIBUTES objectAttributes; 6 InitializeObjectAttributes(&objectAttributes, 7 ®UnicodeString, 8 OBJ_CASE_INSENSITIVE, 9 NULL, NULL); 10 NTSTATUS status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes); 11 if (NT_SUCCESS(status)){ 12 DbgPrint("Open register succeed!\n"); 13 } 14 ULONG ulSize; 15 //第一次查询获取结构体的大小 16 ZwQueryKey(hRegister, KeyFullInformation, NULL, 0, &ulSize); 17 PKEY_FULL_INFORMATION pfi = 18 (PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, ulSize); 19 //第二次查询获取具体的结构体中的数据 20 ZwQueryKey(hRegister, KeyFullInformation, pfi, ulSize, &ulSize); 21 for (ULONG i = 0; i < pfi->SubKeys; i++){ 22 23 ZwEnumerateKey(hRegister, i, 24 KeyBasicInformation, 25 NULL, 0, &ulSize); 26 PKEY_BASIC_INFORMATION pbi = 27 (PKEY_BASIC_INFORMATION) 28 ExAllocatePool(PagedPool, ulSize); 29 ZwEnumerateKey(hRegister, i, 30 KeyBasicInformation, 31 pbi, ulSize, &ulSize); 32 UNICODE_STRING uniKeyName; 33 uniKeyName.Length = uniKeyName.MaximumLength = 34 (USHORT)pbi->NameLength; 35 uniKeyName.Buffer = pbi->Name; 36 DbgPrint("The %d subkey is %wZ\n", i, &uniKeyName); 37 ExFreePool(pbi); 38 } 39 ExFreePool(pfi); 40 ZwClose(hRegister); 41 }
输出结果如下:
枚举子健,代码如下:
1 VOID EnumValueKeyTest(){ 2 UNICODE_STRING regUnicodeString; 3 HANDLE hRegister; 4 RtlInitUnicodeString(®UnicodeString, MY_REG_SOFTWARE_KEY_NAME); 5 OBJECT_ATTRIBUTES objectAttributes; 6 InitializeObjectAttributes(&objectAttributes, 7 ®UnicodeString, 8 OBJ_CASE_INSENSITIVE, 9 NULL, NULL); 10 NTSTATUS status = ZwOpenKey(&hRegister, 11 KEY_ALL_ACCESS, 12 &objectAttributes); 13 if (NT_SUCCESS(status)){ 14 DbgPrint("Open register succeed!\n"); 15 } 16 ULONG ulSize; 17 ZwQueryKey(hRegister, 18 KeyFullInformation, 19 NULL, 0, &ulSize); 20 PKEY_FULL_INFORMATION pfi = 21 (PKEY_FULL_INFORMATION) 22 ExAllocatePool(PagedPool, ulSize); 23 ZwQueryKey(hRegister, 24 KeyFullInformation, 25 pfi, ulSize, &ulSize); 26 for (ULONG i = 0; i < pfi->Values; i++){ 27 ZwEnumerateValueKey(hRegister, i, 28 KeyValueBasicInformation, 29 NULL, 0, &ulSize); 30 PKEY_VALUE_BASIC_INFORMATION pvbi = 31 (PKEY_VALUE_BASIC_INFORMATION) 32 ExAllocatePool(PagedPool, ulSize); 33 ZwEnumerateValueKey(hRegister, i, 34 KeyValueBasicInformation, 35 pvbi, ulSize, &ulSize); 36 UNICODE_STRING uniKeyName; 37 uniKeyName.Length = 38 uniKeyName.MaximumLength = 39 (USHORT)pvbi->NameLength; 40 uniKeyName.Buffer = pvbi->Name; 41 DbgPrint("The %d key value is %wZ", i, uniKeyName); 42 ExFreePool(pvbi); 43 } 44 ExFreePool(pfi); 45 ZwClose(hRegister); 46 }
运行结果如下: