Windows内核开发
第一个驱动程序
环境配置:
安装WDK:
WDK版本与SDK保持一致
然后记得把Spectre Mitigation给Disabled掉,就不用去下载漏洞补丁了。
然后在内核层,警告是会被视为错误的,我们也直接屏蔽掉。
.sys都是作为Windows系统服务存在的,我们需要注册它成为一个服务,这里借助一个工具:A1SysTest v0.3.0.1。
使用DebugView来查看DbgPrint输出的信息。首先要做一些配置:
1.打开 DebugView
2.Capture 菜单
3.打勾 Capture Kernel 和 Enable Verbose Kernel Output 这两个菜单项
4.完成。。。
#include <ntddk.h>
/*
驱动程序和三环程序的区别:
-1.三环程序运行在低2G【每个进程独立的】,驱动程序运行在高2G【进程间共享的】
-2.三环程序每次运行都是一个独立的程序,驱动程序都运行在一个大进程里面【三环加载dll之类的 丢进去了】
*/
/*
@function 驱动程序的停止函数DriverUnload
@param pDriver 当前的驱动对象
*/
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload!\n");
}
/*
@function 驱动程序的主函数DriverEntry
@param pDriver 当前的驱动对象
@param pRegPath 当前的驱动对应的注册表路径
@return NTSTATUS 其实是一个LONG类型
*/
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING pRegPath) {
DbgBreakPoint(); // 这个其实就是一个int 3指令,在反调试的时候有提到 可以使用它对驱动程序进行调试
pDriver->DriverUnload = DriverUnload; // 指定驱动停止函数
DbgPrint("Hello world!\n");
return STATUS_SUCCESS;
}
PDRIVER_OBJECT:
-PVOID DriverStart; 模块基址,相当于PE文件的GetModuleHandle
-ULONG DriverSize; 内存对齐以后的大小
-UNICODE_STRING DriverName; 驱动名字
PUNICODE_STRING
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
/*
typedef struct _UNICODE_STRING {
USHORT Length; 字符串所占空间的大小,不包含字符串结尾 【也就是实际大小】
USHORT MaximumLength; Buffer的大小【也就是创建的缓冲区大小】
#ifdef MIDL_PASS
[size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer; 缓冲区大小
#else // MIDL_PASS
_Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;
*/
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
WCHAR wstr1[100] = L"StarHook"; // 准备一个Unicode类型的数组
// 创建PUNICODE_STRING变量
UNICODE_STRING ustr1 = {0};
ustr1.Buffer = wstr1; // 指向一个缓冲区
ustr1.Length = wcslen(wstr1) * sizeof(WCHAR); // 多少个字符 * Unicode中每一个字符的大小
ustr1.MaximumLength = 100; // 缓存区的大小
DbgPrint("%ws",ustr1.Buffer); // 输出缓存区内容
// 定义一个UNICODE_STRING类型的变量,第一个参数 变量名,第二个参数 Buffer缓存区里面的内容
DECLARE_CONST_UNICODE_STRING(ustr2,L"DECLARE_CONST_UNICODE_STRING");
DbgPrint("%ws", ustr2.Buffer); // 输出缓存区内容
// 定义一个UNICODE_STRING类型的变量, 第一个参数Buffer缓存区里面的内容
UNICODE_STRING ustr3 = RTL_CONSTANT_STRING(L"RTL_CONSTANT_STRING");
DbgPrint("%ws", ustr3.Buffer); // 输出缓存区内容
UNICODE_STRING ustr4;
// 定义一个UNICODE_STRING类型的变量,第一个参数是UNICODE_STRING的类型的地址,第二个是Buffer的内容
RtlInitUnicodeString(&ustr4,L"RtlInitUnicodeString");
DbgPrint("%ws", ustr4.Buffer); // 输出缓存区内容
// UNICODE 和 ANSI的转换
ANSI_STRING astr;
RtlInitAnsiString(&astr, "AnsiString");
UNICODE_STRING ustr;
// 第一个参数UNICODE_STRING地址即接收,第二个参数ANSI_STRING 需要转换的
// 第三个参数是否要分配一个新的缓冲区来存放转换之后的字符串
NTSTATUS ret = RtlAnsiStringToUnicodeString(&ustr,&astr,TRUE);
if (ret == STATUS_SUCCESS) {
// 如果转换成功
DbgPrint("%ws", ustr.Buffer); // 输出缓存区内容
RtlFreeUnicodeString(&ustr); // 释放第三个参数申请出来的内存空间
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
内存分配
应用层的内存分配:
malloc,new都是在堆上分配内存
堆和虚拟内存的关系?堆内存是基于虚拟内存的更小粒度的分割,这个分割由堆管理器来管理。
根据开发者的需要堆管理器会申请一页或多页虚拟内存,然后对这个虚拟内存进行更小粒度的内存分割和管理,以满足开发者对内存的需求。
内核层内存分配:
内核层中不叫堆,称为池,我们可以从池中申请内存,形式与应用层相似。
ExAllocatePool();
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
/*
// 重点就是这两个
typedef enum _POOL_TYPE {
NonPagedPool, 非分页内存 【效率高,存代码的话】
PagedPool, 分页内存 【可以用来存数据,特别要是不经常使用】
} POOL_TYPE;
其实就是我们之前提到的页交换。
当内存满了以后,会把一部分不用的应用程序的数据放到硬盘里面。
而这里的非分页内存,就是不会被放到硬盘里面。
分页内存,就是会被放到硬盘里面。
*/
//PWCHAR pwStr = ExAllocatePool(NonPagedPool,0x100); // 第二个参数是大小
释放内存
//ExFreePool(pwStr);
// 具有标签的内存
PWCHAR pwStr2 = ExAllocatePoolWithTag(NonPagedPool, 0x100,'abcr'); // 第三个参数是标签
if (pwStr2) {
RtlZeroMemory(pwStr2, 0x100); // 初始化内存
// 给内存赋值
RtlCopyMemory(pwStr2, L"StarHook", wcslen(L"StarHook") * sizeof(WCHAR));
// 创建一个UNICODE_STRING 进行初始化 Buffer指向申请出来的内存
UNICODE_STRING ustr = {0};
RtlInitUnicodeString(&ustr,pwStr2); // 指向我们申请的那块内存Buffer
// 可以直接打印UNICODE_STRING 使用wZ
DbgPrint("%wZ", &ustr);
// 释放内存
ExFreePoolWithTag(pwStr2, 'abcr');
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 旁氏列表内存分配
// 适用于高频率从系统内存池中申请固定大小内存
// 1.初始化一个旁氏列表对象
// 要求一定是要非页交换的,即NonPagedPool
PNPAGED_LOOKASIDE_LIST pgList = ExAllocatePool(NonPagedPool,sizeof(NPAGED_LOOKASIDE_LIST));
if (pgList) {
// 1.旁氏列表结构体 2.分配的函数 NULL就好 3.释放的函数 NULL就好
// 4.标志 0就好 5.大小 6.TAG 7.保留 必须为0
ExInitializeNPagedLookasideList(pgList, NULL, NULL, 0, 0x100, '123', 0);
// 2.通过旁氏列表对象申请内存 后面每次申请都是通过旁氏列表对象 所以申请的大小都一样
PWCHAR wcstr = ExAllocateFromNPagedLookasideList(pgList);
if (wcstr) {
RtlZeroMemory(wcstr, 0x100);
RtlCopyMemory(wcstr,L"StarHook",wcslen(L"StarHook") * sizeof(WCHAR));
UNICODE_STRING ustr = {0};
RtlInitUnicodeString(&ustr, wcstr);
DbgPrint("%wZ", ustr);
// 旁氏列表对象,要释放的条目
ExFreeToNPagedLookasideList(pgList,wcstr);
}
// 释放旁氏列表对象
ExDeleteNPagedLookasideList(pgList);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 1.创建一个旁氏列表对象 【可以页交换的】
PPAGED_LOOKASIDE_LIST pgList = ExAllocatePool(PagedPool, sizeof(PAGED_LOOKASIDE_LIST));
if (pgList) {
// 2.初始化旁氏列表对象
ExInitializePagedLookasideList(pgList, NULL, NULL, 0, 0x100, 'star', 0);
// 3.就可以通过旁氏列表对象 创建固定大小的内存能存了
PWCHAR pwstr = ExAllocateFromPagedLookasideList(pgList);
if (pwstr) {
RtlZeroMemory(pwstr, 0x100);
RtlCopyMemory(pwstr, L"StarHook", wcslen(L"StarHook") * sizeof(WCHAR));
UNICODE_STRING ustr = {0};
RtlInitUnicodeString(&ustr, pwstr);
DbgPrint("%wZ", ustr);
// 4.释放申请出来的内存
ExFreeToPagedLookasideList(pgList, pwstr);
}
// 5.销毁旁氏列表对象
ExDeletePagedLookasideList(pgList);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
链表LIST_ENTRY
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
typedef struct _TEST {
ULONG m_dataA;
ULONG m_dataB;
LIST_ENTRY m_listEntry;
ULONG m_dataC;
}TEST,*PTEST;
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
/*
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink; 下一个
struct _LIST_ENTRY *Blink; 上一个
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
这就像一辆货车,和数据脱离开来了,我不管你给我放什么数据,我就是一辆车。
你把数据放上来就好了,你要访问数据通过偏移的形式去访问就行。
*/
PLIST_ENTRY pHeader = ExAllocatePool(NonPagedPool, sizeof(LIST_ENTRY)); // 创建一个非页交换节点
TEST test1 = { 0 };
TEST test2 = { 0 };
TEST test3 = { 0 };
TEST test4 = { 0 };
test1.m_dataA = 0x123;
test2.m_dataB = 0x456;
test3.m_dataA = 0x789;
test4.m_dataA = 0x321;
if (pHeader) {
RtlZeroMemory(pHeader, sizeof(LIST_ENTRY)); // 初始化内存
// 初始化头结点 实际上就是让 下一个指针和上一个指针都指向自身
// pHeader->Blink = pHeader->Flink = pHeader;
InitializeListHead(pHeader);
// 插入到头部
InsertHeadList(pHeader,&test1.m_listEntry);
InsertHeadList(pHeader,&test2.m_listEntry);
InsertHeadList(pHeader,&test3.m_listEntry);
InsertHeadList(pHeader,&test4.m_listEntry);
// 因为每次都是插入到头部 所以现在顺序是 头结点->test4->test3->test2->test1
PLIST_ENTRY pListEntry = pHeader->Flink;
while (pListEntry != pHeader) { // 双向链表,最后一个节点的next = 头结点 当还没有遍历到头结点
// 微软帮我们封装了偏移,直接通过这个可以定位到 结构体的首地址
// 当前链表指针 结构体类型 listEntnry在结构体中的名字
PTEST pTest = CONTAINING_RECORD(pListEntry, TEST, m_listEntry);
DbgPrint("m_dataA = %x,m_dataB = %x,m_dataC = %x\n",
pTest->m_dataA, pTest->m_dataB, pTest->m_dataC);
// 移动到下一个节点
pListEntry = pListEntry->Flink;
}
// RemoveHeadList 移除头结点
// RemoveTailist 移除尾节点
// RemoveEntryList 移除指定节点
// 释放内存
ExFreePool(pHeader);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
二叉查找树
RTL_GENERIC_TABLE
是一个二叉树结构
伸展树(Splay Tree)是一种二叉查找树,这种结构常用在文件系统。
每次查找之后对树进行重构,把被查找的条目搬移到离根节点近的位置。伸展树是一种自调节形式的二叉查找树。
他会沿着某个节点到根节点之间的路径,通过一系列的旋转把这个节点移动到根节点附近。
【自己感觉其实有点像哈夫曼树,每次查找就是一次权重的更新吧,然后把权重大的放在离根节点近的位置】
第一个节点:申请内存->插入
后面的节点:比较看这个元素是否存在->不存在申请内存->插入
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
typedef struct _Test {
ULONG a;
ULONG b;
}TEST,*PTEST;
// 比较函数
RTL_GENERIC_COMPARE_RESULTS NTAPI RtlCmp(
struct _RTL_GENERIC_TABLE* Table,
PVOID FirstStruct,
PVOID SecondStruct
) {
DbgPrint("RtlCmp");
PTEST first = FirstStruct;
PTEST second = SecondStruct;
if (first->a == second->a) {
// 如果a的值相同 我们就认为相同
return GenericEqual;
}else if (first->a > second->a) {
return GenericGreaterThan;
}
return GenericLessThan;
}
// 申请内存函数
PVOID NTAPI RtlAlloc(
struct _RTL_GENERIC_TABLE* Table,
CLONG ByteSize
) {
DbgPrint("RtlAlloc");
return ExAllocatePool(NonPagedPool, ByteSize);
}
// 释放内存函数
VOID NTAPI RtlFree(
struct _RTL_GENERIC_TABLE* Table,
__drv_freesMem(Mem) _Post_invalid_ PVOID Buffer
) {
DbgPrint("RtlFree");
ExFreePool(Buffer);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
TEST test1 = {1,2};
TEST test2 = {2,2};
TEST test3 = {3,2};
TEST test4 ={4,2};
BOOLEAN isNew = FALSE;
PRTL_GENERIC_TABLE pGenericRootTree = ExAllocatePool(NonPagedPool, sizeof(RTL_GENERIC_TABLE));
if (pGenericRootTree) {
RtlZeroMemory(pGenericRootTree, sizeof(RTL_GENERIC_TABLE)); // 初始化内存
// 初始化二叉查找树
// 1.二叉查找树结构体指针 2.比较函数【一样就不插入】 3.申请内存函数 4.释放内存函数 5.自定义上下文,不用就NULL
RtlInitializeGenericTable(pGenericRootTree, RtlCmp, RtlAlloc, RtlFree,NULL);
// 2.插入元素 根节点 要插入元素 结构体大小 返回值这个元素是否已经存在【不存在返回TRUE】
RtlInsertElementGenericTable(pGenericRootTree,&test1,sizeof(TEST),&isNew);
RtlInsertElementGenericTable(pGenericRootTree,&test2,sizeof(TEST),&isNew);
RtlInsertElementGenericTable(pGenericRootTree,&test3,sizeof(TEST),&isNew);
RtlInsertElementGenericTable(pGenericRootTree,&test4,sizeof(TEST),&isNew);
// 获取节点数量
ULONG treeElements = RtlNumberGenericTableElements(pGenericRootTree);
DbgPrint("当前树的节点数量:%d", treeElements);
// 遍历树
// 获取第几个序号的节点
for (ULONG index = 0; index < treeElements; index++) {
PTEST pElement = RtlGetElementGenericTable(pGenericRootTree,index);
DbgPrint("当前节点的a值:%d", pElement->a);
}
// 遍历2
ULONG RestartKey = 0; // 从头开始 设置为0
PTEST pElement = NULL;
for (pElement = RtlEnumerateGenericTableWithoutSplaying(pGenericRootTree, &RestartKey);
pElement != NULL;
pElement = RtlEnumerateGenericTableWithoutSplaying(pGenericRootTree, &RestartKey)) {
DbgPrint("当前节点的b值:%d", pElement->b);
}
// 查询节点
pElement = RtlLookupElementGenericTable(pGenericRootTree,&test2);
if (pElement != NULL) { // 其实都是根据我们都比较函数来查找的
DbgPrint("查询到节点,a值:%d", pElement->a);
}else {
DbgPrint("查询不到节点");
}
// RtlDeleteElementGenericTable 删除节点
// RtlIsGenericTableEmpty 当前二叉查找树是否为空
// 释放内存
ExFreePool(pGenericRootTree);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
AVL树:平衡二叉查找树
普通的二叉查找树没有平衡的限制,很可能在某些特殊的情况下退化成一条类似链表的树。这些搜索效率就极为低下。
文件操作
句柄:
像我们之前在三环CreateFile之类的时候会返回一个句柄
内核对象存放在内核空间。 APU HANDLE
就像我们保护模式中
线性地址->物理地址
句柄->内核对象
也就是说可以理解为句柄映射到一个内核对象。
A:HANDLE hEvent = CreateEvent() hEvent = 0x1234
B:
A进程的hEvent的句柄值0x1234在B进程是用不了的。
每一个进程都有自己的一个句柄表。
所以每个进程在使用句柄的时候都会找到自己当前这个进程对应的句柄表,然后再找到对应的内核对象。
同理,我们0环也有自己定句柄表,不过我们前面也是了,三环里面每一个进程都是独立的。
而0环,是在一个大进程里面的。它只有一个句柄表,所有的进程共享。
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 打开/创建一个文件
/*
NTSYSAPI NTSTATUS ZwCreateFile(
[out] PHANDLE FileHandle, 输出参数 文件句柄
[in] ACCESS_MASK DesiredAccess, 文件操作权限
[in] POBJECT_ATTRIBUTES ObjectAttributes, 设置句柄的相关属性
[out] PIO_STATUS_BLOCK IoStatusBlock, 调用状态 输出参数
[in, optional] PLARGE_INTEGER AllocationSize, 文件初始分配的大小 填0就好
[in] ULONG FileAttributes, 文件属性
[in] ULONG ShareAccess, 文件共享权限
[in] ULONG CreateDisposition, 创建环境【新建开始打开还是】
[in] ULONG CreateOptions, 创建选项【打开的是文件还是目录】
[in, optional] PVOID EaBuffer, 固定NULL
[in] ULONG EaLength 固定0
);
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length; 相当于dwSize指向结构体的大小
HANDLE RootDirectory; 文件路径句柄
PUNICODE_STRING ObjectName; 相对于RootDirectory的相对路径.Root填NULL,这里填绝对路径就好
ULONG Attributes; 句柄属性
PVOID SecurityDescriptor; 安全描述符NULL
PVOID SecurityQualityOfService; NULL
} OBJECT_ATTRIBUTES;
*/
HANDLE hFileHandle = 0;
OBJECT_ATTRIBUTES oa = {0};
oa.Length = sizeof(OBJECT_ATTRIBUTES);
oa.RootDirectory = NULL;
UNICODE_STRING objPath = RTL_CONSTANT_STRING(L"\\??\\c:\\1.txt");
oa.ObjectName = &objPath;
oa.Attributes = OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE; // 路径不区分大小写 | 内核权限句柄
oa.SecurityDescriptor = NULL;
oa.SecurityQualityOfService = NULL;
IO_STATUS_BLOCK isb = {0};
NTSTATUS ret = ZwCreateFile(&hFileHandle, GENERIC_ALL, &oa, &isb, 0,
FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,FILE_OPEN_IF,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, // 打开文件 | 同步
NULL,0);
if (ret == STATUS_SUCCESS) {
// 如果打开/创建文件成功 就是读写文件了 接着
DbgPrint("打开/创建文件成功");
// 文件句柄 异步操作 异步操作 异步操作 输出参数 缓冲区 缓冲区大小 文件偏移 KEY给NULL就好
IO_STATUS_BLOCK isbRead = { 0 };
LARGE_INTEGER offset = {0};
PVOID fileBuff = ExAllocatePool(NonPagedPool, 0x1000);
if (fileBuff) {
RtlZeroMemory(fileBuff, 0x1000);
ret = ZwReadFile(hFileHandle, NULL, NULL, NULL, &isbRead, fileBuff, 0x1000, &offset, NULL);
if (ret == STATUS_SUCCESS) {
DbgPrint("读取文件内容成功");
DbgBreakPoint();
}else {
DbgPrint("读取文件内容失败");
}
}
// 关闭文件
ZwClose(hFileHandle);
ExFreePool(fileBuff); // 释放内存
}else {
DbgPrint("打开/创建文件失败");
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
文件操作2
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 打开/创建一个文件
/*
NTSYSAPI NTSTATUS ZwCreateFile(
[out] PHANDLE FileHandle, 输出参数 文件句柄
[in] ACCESS_MASK DesiredAccess, 文件操作权限
[in] POBJECT_ATTRIBUTES ObjectAttributes, 设置句柄的相关属性
[out] PIO_STATUS_BLOCK IoStatusBlock, 调用状态 输出参数
[in, optional] PLARGE_INTEGER AllocationSize, 文件初始分配的大小 填0就好
[in] ULONG FileAttributes, 文件属性
[in] ULONG ShareAccess, 文件共享权限
[in] ULONG CreateDisposition, 创建环境【新建开始打开还是】
[in] ULONG CreateOptions, 创建选项【打开的是文件还是目录】
[in, optional] PVOID EaBuffer, 固定NULL
[in] ULONG EaLength 固定0
);
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length; 相当于dwSize指向结构体的大小
HANDLE RootDirectory; 文件路径句柄
PUNICODE_STRING ObjectName; 相对于RootDirectory的相对路径.Root填NULL,这里填绝对路径就好
ULONG Attributes; 句柄属性
PVOID SecurityDescriptor; 安全描述符NULL
PVOID SecurityQualityOfService; NULL
} OBJECT_ATTRIBUTES;
*/
HANDLE hFile = 0;
OBJECT_ATTRIBUTES oa = {0};
oa.Length = sizeof(OBJECT_ATTRIBUTES);
oa.RootDirectory = NULL;
UNICODE_STRING objPath = RTL_CONSTANT_STRING(L"\\??\\c:\\1.txt");
oa.ObjectName = &objPath;
oa.Attributes = OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE; // 不区分大小写 和内核权限句柄
oa.SecurityDescriptor = NULL;
oa.SecurityQualityOfService = NULL;
IO_STATUS_BLOCK pibFile = {0};
NTSTATUS ret = ZwCreateFile(&hFile,GENERIC_ALL,&oa,&pibFile,NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,
FILE_OVERWRITE_IF, // 不存在创建,存在覆盖
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, // 打开的是文件而是同步
NULL,0);
if (NT_SUCCESS(ret)) {
// 操作文件 写入
IO_STATUS_BLOCK pibWrite = {0};
LARGE_INTEGER li = {0};
ret = ZwWriteFile(hFile, NULL, NULL, NULL, &pibWrite,
L"Hello,StarHook",wcslen(L"Hello,StarHook") * sizeof(WCHAR), &li, NULL);
if (NT_SUCCESS(ret)) {
DbgPrint("写入成功");
}else {
DbgPrint("写入失败");
}
// 关闭句柄
ZwClose(hFile);
}else {
DbgPrint("打开文件失败");
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
获取文件大小:
IO_STATUS_BLOCK pibQuery = { 0 };
FILE_STANDARD_INFORMATION fsInfo = { 0 };
// 文件句柄 输出参数 缓存区 缓冲区大小 要查询什么信息【文件标准信息】
ret = ZwQueryInformationFile(hFile, &pibQuery,&fsInfo,sizeof(FILE_STANDARD_INFORMATION),FileStandardInformation);
if (NT_SUCCESS(ret)) {
DbgPrint("文件大小:%lld", fsInfo.EndOfFile.QuadPart);
}
文件操作3
那么我们读写文件可以一次性读取完毕,然后一次性写入
我们也可以通过IO_STATUS_BLOCK获取实际读取到大小,如果读取成功,每次我们就写入实际读取到的大小,循环操作
拷贝文件。
#include <ntddk.h>
/*
@function 创建/打开文件
@param [int,out] phFile 文件句柄
@param [in] objPath 文件路径
@param [in] isbFile 状态
@return 是否创建成功
*/
NTSTATUS MyZwCreateFile(PHANDLE phFile,PUNICODE_STRING objPath, PIO_STATUS_BLOCK isbFile) {
OBJECT_ATTRIBUTES oa = {0};
oa.Length = sizeof(OBJECT_ATTRIBUTES);
oa.Attributes = OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE; // 忽略大小写 和 内核句柄
oa.RootDirectory = NULL;
oa.ObjectName = objPath;
oa.SecurityDescriptor = NULL;
oa.SecurityQualityOfService = NULL;
return ZwCreateFile(phFile, GENERIC_ALL, &oa, isbFile, NULL,FILE_ATTRIBUTE_NORMAL,
NULL, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,NULL,0);
}
/*
@function 文件拷贝
@return 是否拷贝成功
*/
NTSTATUS MyZwCopyFile(PUNICODE_STRING objPath1, PUNICODE_STRING objPath2) {
NTSTATUS status;
HANDLE hRead, hWrite;
IO_STATUS_BLOCK isbFile = {0};
status = MyZwCreateFile(&hRead, objPath1, &isbFile);
if (!NT_SUCCESS(status)) {
return status;
}
status = MyZwCreateFile(&hWrite, objPath2, &isbFile);
if (!NT_SUCCESS(status)) {
return status;
}
ULONG buffLen = 0x1000;
PVOID buffer = ExAllocatePool(NonPagedPool,buffLen);
if (buffer == NULL) {
return -1;
}
RtlZeroMemory(buffer, buffLen);
LARGE_INTEGER li = {0};
// 读写文件
while (TRUE) {
status = ZwReadFile(hRead, NULL, NULL, NULL, &isbFile, buffer,buffLen, &li,NULL);
if (!NT_SUCCESS(status)) { // 文件读取到结尾了也会出错
if (status == STATUS_END_OF_FILE) {
// 正常读取完毕文件
DbgPrint("文件拷贝完毕");
status = STATUS_SUCCESS;
}
break;
}
// 拷贝 isbFile.Information 真实读取了多少
status = ZwWriteFile(hWrite, NULL, NULL, NULL, &isbFile, buffer, isbFile.Information, &li, NULL);
if (!NT_SUCCESS(status)) {
break;
}
// 移动文件指针偏移位置
li.QuadPart += isbFile.Information;
}
if(buffer) ExFreePool(buffer);
if(hRead) ZwClose(hRead);
if(hWrite) ZwClose(hWrite);
return status;
}
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 拷贝
UNICODE_STRING readPath = RTL_CONSTANT_STRING(L"\\??\\c:\\1.txt");
UNICODE_STRING writePath = RTL_CONSTANT_STRING(L"\\??\\c:\\1_new.txt");
MyZwCopyFile(&readPath, &writePath);
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
注册表操作
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 创建或打开注册表
HANDLE hReg = 0;
OBJECT_ATTRIBUTES oa = {0};
oa.Length = sizeof(OBJECT_ATTRIBUTES);
oa.Attributes = OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE;
oa.SecurityDescriptor = NULL;
oa.SecurityQualityOfService = NULL;
oa.RootDirectory = NULL;
// HKEY_LOCAL_MACHINE\SOFTWARE\Star
UNICODE_STRING objPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SOFTWARE\\Star");
oa.ObjectName = &objPath;
// 1.句柄 2.操作权限 3.OBJECT_ATTRIBUTES指针类型 4.0 5.0 6.创建标志 7.新建还是打开KEY【输出参数】
/*
6.创建标志
REG_OPTION_VOLATILE 重新启动系统时不会保留密钥。
REG_OPTION_NON_VOLATILE 重新启动系统时会保留密钥。
7.新建还是打开KEY
REG_CREATED_NEW_KEY 创建了一个新密钥。
REG_OPENED_EXISTING_KEY 已打开现有密钥。
*/
ULONG Disposition = 0;
NTSTATUS ret = ZwCreateKey(&hReg,KEY_ALL_ACCESS,&oa,0,0,REG_OPTION_NON_VOLATILE,&Disposition);
if (NT_SUCCESS(ret)) {
// 那么就成功打开了注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Star 这个目录
if (Disposition == REG_CREATED_NEW_KEY) {
DbgPrint("创建了新的Key=>\\SOFTWARE\\Star");
}else if(Disposition == REG_OPENED_EXISTING_KEY){
DbgPrint("打开已经存在Key=>\\SOFTWARE\\Star");
}
// 关闭句柄
ZwClose(hReg);
}else {
DbgPrint("KEY => \\SOFTWARE\\Star 创建失败");
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
DbgPrint("DriverUnload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 打开注册表
HANDLE hReg = 0;
OBJECT_ATTRIBUTES oa = {0};
oa.Length = sizeof(OBJECT_ATTRIBUTES);
oa.RootDirectory = NULL;
UNICODE_STRING objPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SOFTWARE\\Star");
oa.ObjectName = &objPath;
oa.Attributes = OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE;
oa.SecurityDescriptor = NULL;
oa.SecurityQualityOfService = NULL;
// ULONG Disposition = 0;
// NTSTATUS ret = ZwCreateKey(&hReg,KEY_ALL_ACCESS,&oa,0,0, REG_OPTION_NON_VOLATILE,&Disposition);
// 打开Key 要求一定存在,ZwCreateKey存在打开,不存在创建
NTSTATUS ret = ZwOpenKey(&hReg, GENERIC_ALL, &oa);
if (NT_SUCCESS(ret)) {
DbgPrint("打开key=>\\SOFTWARE\\Star");
// 操作内容
// 句柄 key的名称 固定0 类型 Value【Key对应的数据】 Value数据的大小
UNICODE_STRING key = RTL_CONSTANT_STRING(L"name");
// REG_SZ 字符串类型 字符串长度要计算上 \0的长度
ret = ZwSetValueKey(hReg, &key, 0, REG_SZ,L"StarHook",(wcslen(L"StarHook") + 1)* sizeof(WCHAR));
if (NT_SUCCESS(ret)) {
DbgPrint("写入注册表成功");
// 查询数据
// 句柄 要查询的key 要查询那一列的信息 结果缓冲区 缓冲区的长度 实际上需要多大
PVOID dataBuff = ExAllocatePool(NonPagedPool,0x100);
RtlZeroMemory(dataBuff,0x100);
ULONG resultLong = 0;
ret = ZwQueryValueKey(hReg, &key, KeyValuePartialInformation,
dataBuff,0x100,&resultLong);
if (NT_SUCCESS(ret)) { // 如果查询成功
PKEY_VALUE_PARTIAL_INFORMATION pi = dataBuff;
if(pi->Type == REG_SZ)
DbgPrint("%ls", pi->Data);
}
ExFreePool(dataBuff);
}
// 关闭句柄
ZwClose(hReg);
}else{
DbgPrint("打开失败");
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)