【笔记】windows驱动编程学习-从hello world出发
本帖最后由 jy40 于 2020-3-9 10:36 编辑最近在学习windwos下的驱动编程
============
开个贴记录一下一些相关的知识点,以及遇到的问题(各种蓝屏的姿势)
---------
配置好VS、WDK以及双机调试环境后,开始学习
首先,从最简单的驱动程序开始,写个Hello World
#include<ntddk.h>
VOID DriverUnload(PDRIVER_OBJECT pdriver) {
DbgPrint("Unload Success\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pdriver, PUNICODE_STRING pReg) {
DbgPrint("Hello World\n");
pdriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
按f7编译生成以后,即可在自己的目录下寻找到 .sys后缀的文件,这就是我们生成的驱动文件了,同样是pe文件的格式
此时进入我们的虚拟机,我这里用的是win xp sp3环境进行驱动开发学习的,将.sys驱动复制进去,使用驱动装载器将我们的驱动装载
驱动装载器可以去github上下载,搜索关键词"DriverLoader"即可搜索到结果,在后续弄明白驱动装载的原理后,也打算实现一个自己的驱动装载工具
点击Load即为装载驱动,Unload即为卸载驱动
可以使用DbgView来查看我们的结果,需要在使用的时候按Ctrl+K或者是勾选图中的Capture Kernel选项。
当然,这时候踩到了第一个坑,驱动装载后,直接蓝屏了!
原来是由于实验环境是win xp环境,而vs2019默认的目标环境是win10,我们需要在设置里修改目标平台属性
经过测试,指定目标平台版本为windows 7也可以在win xp系统中运行
再次装载驱动,即可在DbgView中看到打印出的Hello World字符
第一个程序所包含的知识点
---------------------
**DriverEntry:**
该函数就相当于C语言中的main函数,相当于驱动模块的入口点,在装载驱动的时候执行其中的代码。该函数有两个参数,一个是驱动对象的指针pDriver,该结构会在后续为大家介绍,还有一个参数是指向UNICODE字符串的指针,储存了驱动注册到的注册表目录,可以使用DbgPrint函数以%zW参数输出
**DbgPrint:**
相当于写3环c程序中的printf程序
**DriverUnload:**
驱动卸载时所执行的函数,定义后需要在入口函数中将卸载函数的地址赋值给pdriver->DriverUnload
点击驱动装载器的Unload,即可在DbgView中看到结果
此时,我们的第一个驱动程序的执行周期算是结束了,从装载的时候执行DriverEntry到卸载的时候执行DriverUnload函数,接下来,就是对pDriver对象进行研究
我们可以使用DbgPrint("%X",pdriver);将指针的值打印出来
81240DA0就是pDriver对象的地址,在windbg中中断下来,使用dt _DRIVER_OBJECT 81240DA0指令查看该结构的内容,结果如下
可以看到驱动名等一些驱动的参数,比如驱动名,比如驱动的卸载函数的地址,我们反编译一下他
可以很清晰的看到调用了一个DbgPrint函数,而push的则是我们的字符串参数,感兴趣的可以自己尝试跟进一下查看字符串的值是否为"Unload Success“
这里我们对DriverSection进行着重研究
DriverSection是个结构体,指向了 _LDR_DATA_TABLE_ENTRY结构,使用dt _LDR_DATA_TABLE_ENTRY 0x8129d4a8 查看结构内容
可以看到在DriverSection结构体的首部是一个链表,查阅资料得知,这是一个指向系统中已经加载驱动的双向链表,我们可以通过遍历这个列表得到系统中已经加载的所有驱动模块,读者可以自己使用dt指令对双向链表的节点进行观察
看到这个,可以进行下拓展,写个驱动模块以遍历出系统中已经安装的驱动名称等信息,或者将自己的驱动节点从该链表中断链以达到隐藏的目的(听说断链技术已经不适用于win 10,会导致蓝屏)
遍历的代码如下
首先需要自己定义一个DriverSection结构体,这个结构体windows未导出
typedef struct _DRIVER_SECTION {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union {
LIST_ENTRY HashLinks;
struct {
PVOID SectionPointer;
ULONG CheckSum;
};
};
ULONG TimeDateStamp;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
}LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) {
pDriver->DriverUnload = DriverUnload;
PLDR_DATA_TABLE_ENTRY MyDriverList = (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
while (1) {
DbgPrint("%wZ", &MyDriverList->BaseDllName);
MyDriverList = MyDriverList->InLoadOrderLinks.Flink;
if (MyDriverList == (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection) {
DbgPrint("end\n");
break;
}
}
return STATUS_SUCCESS;
}
遍历的结果如图所示,就不截全了
驱动断链的代码如下
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) {
pDriver->DriverUnload = DriverUnload;
PLDR_DATA_TABLE_ENTRY MyDriverList = (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
MyDriverList->InLoadOrderLinks.Blink->Flink = MyDriverList->InLoadOrderLinks.Flink;
MyDriverList->InLoadOrderLinks.Flink->Blink = MyDriverList->InLoadOrderLinks.Blink;
return STATUS_SUCCESS;
}
实验时,先将断链的驱动装载至其中,然后再重新装载遍历驱动对象的模块
实验结果如下
可以看到无法寻找到我们的HideMyself.sys模块,隐藏成功
然而这种隐藏并不是非常实用的,首先前面提到过的,无法在win10 x64下使用;其次驱动对象pDriver头4个成员的值是固定的,会被内存搜索的方式暴力定位到驱动对象,只能对抗一些API的检索,为了防止被这种方式搜索到,可以考虑将pDriver的头4个成员置零;最后,系统注册一个驱动的时候,除了向该链表添加节点外,还有对注册表进行写入的操作,所以注册表也会暴露驱动模块的存在,要想实现真正的隐藏必须要注意这一点
萌新第一次发帖,如有不足希望大佬提出意见以补足
-------------- 贼强啊 谢谢分享,驱动开发挺难的,学习
满满的干货啊,收藏了! 谢谢分享,大佬nb 厉害,厉害 小白可以学这东西? 如果用这种方式。后期不优化,那么必须神机才能装载。占用资源过高。
谢谢分享,大佬 有点意思, 看上去挺复杂的
页:
[1]
2