和应用程序一样,取得程序也要经常和文件和注册表打交道。只不过在Ring0下对这些的操作和在Ring3下的操作不同。这里简单介绍下内核模式下文件和注册表的操作。
首先是在驱动中读写文件。首先是打开文件的操作。由于在内核并不能直接接受一个字符串,所以我们必须填写一个OBJECT_ATTRIBUTES 结构,这个结构必须先被InitializeObjectAttributes初始化。
然后在内核中文件操作的函数有——打开文件:ZwCreateFile ;读文件:ZwReadFile ;写文件:ZwWriteFile ;获取文件属性:ZwQueryInformationFile ;修改文件属性:ZwSetInformationFile 。下面来看段代码说明问题:
BOOLEAN
MyCopyFile(
IN PUNICODE_STRING ustrDestFile,
IN PUNICODE_STRING ustrSrcFile
)
{
HANDLE hSrcFile, hDestFile;
PVOID buffer = NULL;
ULONG length = 0;
LARGE_INTEGER offset = {0};
IO_STATUS_BLOCK Io_Status_Block = {0};
OBJECT_ATTRIBUTES obj_attrib;
NTSTATUS status;
BOOLEAN bRet = FALSE;
FILE_BASIC_INFORMATION fpi;
do
{
// 打开源文件
InitializeObjectAttributes(&obj_attrib,
ustrSrcFile,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwCreateFile(&hSrcFile,
GENERIC_READ,
&obj_attrib,
&Io_Status_Block,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (!NT_SUCCESS(status))
{
bRet = FALSE;
goto END;
}
// 打开目标文件
InitializeObjectAttributes(&obj_attrib,
ustrDestFile,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwCreateFile(&hDestFile,
GENERIC_WRITE,
&obj_attrib,
&Io_Status_Block,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN_IF,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (!NT_SUCCESS(status))
{
bRet = FALSE;
goto END;
}
// 为buffer分配10KB空间
buffer = ExAllocatePool(NonPagedPool, 1024 * 10);
if (buffer == NULL)
{
bRet = FALSE;
goto END;
}
// 复制文件
while (1)
{
length = 10 * 1024;
// 读取源文件
status = ZwReadFile(hSrcFile,
NULL,
NULL,
NULL,
&Io_Status_Block,
buffer,
length,
&offset,
NULL);
if (!NT_SUCCESS(status))
{
// 如果状态为STATUS_END_OF_FILE,说明文件已经读取到末尾
if (status == STATUS_END_OF_FILE)
{
bRet = TRUE;
goto END;
}
}
// 获得实际读取的长度
length = Io_Status_Block.Information;
// 写入到目标文件
status = ZwWriteFile(hDestFile,
NULL,
NULL,
NULL,
&Io_Status_Block,
buffer,
length,
&offset,
NULL);
if (!NT_SUCCESS(status))
{
bRet = FALSE;
goto END;
}
// 移动文件指针
offset.QuadPart += length;
}
//读取文件长度
status = ZwQueryInformationFile(hDestFile,
&Io_Status_Block,
&offset,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
fpi.FileAttributes = FILE_ATTRIBUTE_HIDDEN; //设置文件属性为隐藏,可是实际操作中并不能成功,我还没找到问题的解决方法。
status = ZwSetInformationFile(hDestFile,
&Io_Status_Block,
&fpi,
sizeof(FILE_BASIC_INFORMATION),
FileBasicInformation);
if (NT_SUCCESS(status))
{
KdPrint((”update the file successfully./n”));
goto END;
}
} while (0);
END:
if (hSrcFile)
{
ZwClose(hSrcFile);
}
if (hDestFile)
{
ZwClose(hDestFile);
}
if (buffer = NULL)
{
ExFreePool(buffer);
}
return bRet;
}
调用代码如下:
RtlInitUnicodeString(&ustrSrcFile, L” //??//C://windows//system32//cmd.exe “);枚举注册表子项使用 ZwQueryKey和 ZwEnumerateKey配合完成,枚举子键使用 ZwQueryKey和 ZwEnumerateValueKey配合完成。
值得注意的是在内核编程里注册表各子键的路径和在Ring3下的不一样,
HEKY_LOCAL_MACHINE对应在内核编程里的写法是/Registery/Machine
HEKY_USERS对应在内核编程里的写法是/Registry/User
HEKY_CLASSES_ROOT和HEKY_CURRENT_USER在内核编程中没有对应的路径,但是可以通过其他方法得到。
下面给出个简单的代码来实现注册表的枚举:
NTSTATUS
RegEnumTest() //枚举注册表子项函数
{
UNICODE_STRING ustrRegString;
UNICODE_STRING ustrKeyName;
HANDLE hRegister;
ULONG ulSize, i = 0;
OBJECT_ATTRIBUTES obj_attrib;
NTSTATUS status;
PKEY_FULL_INFORMATION pfi;
PKEY_BASIC_INFORMATION pbi;
// 初始化
RtlInitUnicodeString(&ustrRegString,L”//Registry//Machine//SOFTWARE//360Safe “);
InitializeObjectAttributes(&obj_attrib,
&ustrRegString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
// 打开注册表
status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &obj_attrib);
if (NT_SUCCESS(status))
{
KdPrint((”[Test] ZwOpenKey %wZ Success!”, ustrRegString));
}
// 第一次调用是为了获取需要的长度
ZwQueryKey(hRegister, KeyFullInformation, NULL, 0, &ulSize);
pfi = (PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, ulSize);
// 第二次调用是为了获取数据
ZwQueryKey(hRegister, KeyFullInformation, pfi, ulSize, &ulSize);
for (i = 0; i < pfi->SubKeys; i++)
{
// 获取第i个子项的长度
ZwEnumerateKey(hRegister, i, KeyBasicInformation, NULL, 0, &ulSize);
pbi = (PKEY_BASIC_INFORMATION)ExAllocatePool(PagedPool, ulSize);
// 获取第i个子项的数据
ZwEnumerateKey(hRegister, i, KeyBasicInformation, pbi, ulSize, &ulSize);
ustrKeyName.Length = (USHORT)pbi->NameLength;
ustrKeyName.Buffer = pbi->Name;
KdPrint((”[Test] The %d SubItem Name : %wZ./n”, i, &ustrKeyName));
// 释放内存
ExFreePool(pbi);
}
ExFreePool(pfi);
ZwClose(hRegister);
return STATUS_SUCCESS;
}
————————————————————————————————————
NTSTATUS
RegEnumSubValueTest() //枚举注册表子键函数
{
UNICODE_STRING RegUnicodeString;
HANDLE hRegister;
ULONG ulSize;
NTSTATUS ntStatus;
UNICODE_STRING uniKeyName;
PKEY_VALUE_BASIC_INFORMATION pvbi;
PKEY_FULL_INFORMATION pfi;
ULONG i;
OBJECT_ATTRIBUTES objectAttributes;
//初始化UNICODE_STRING字符串
RtlInitUnicodeString( &RegUnicodeString,
L”//Registry//Machine//SOFTWARE//360Safe “);
//初始化objectAttributes
InitializeObjectAttributes(&objectAttributes,
&RegUnicodeString,
OBJ_CASE_INSENSITIVE,//对大小写敏感
NULL,
NULL );
//打开注册表
ntStatus = ZwOpenKey( &hRegister,
KEY_ALL_ACCESS,
&objectAttributes);
if (NT_SUCCESS(ntStatus))
{
KdPrint((”Open register successfully/n”));
}
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++)
{
ZwEnumerateValueKey(hRegister,
i,
KeyValueBasicInformation,
NULL,
0,
&ulSize);
pvbi =(PKEY_VALUE_BASIC_INFORMATION)
ExAllocatePool(PagedPool,ulSize);
ZwEnumerateValueKey(hRegister,
i,
KeyValueBasicInformation,
pvbi,
ulSize,
&ulSize);
uniKeyName.Length =
uniKeyName.MaximumLength =
(USHORT)pvbi->NameLength;
uniKeyName.Buffer = pvbi->Name;
KdPrint((”The %d sub value name:%wZ/n”,i,&uniKeyName));
if (pvbi->Type==REG_SZ)
{
KdPrint((”The sub value type:REG_SZ/n”));
}else if (pvbi->Type==REG_MULTI_SZ)
{
KdPrint((”The sub value type:REG_MULTI_SZ/n”));
}else if (pvbi->Type==REG_DWORD)
{
KdPrint((”The sub value type:REG_DWORD/n”));
}else if (pvbi->Type==REG_BINARY)
{
KdPrint((”The sub value type:REG_BINARY/n”));
}
ExFreePool(pvbi);
}
ExFreePool(pfi);
ZwClose(hRegister);
return STATUS_SUCCESS;
}