[原创]W2k Driving 学习笔记(二)使用GCC创建 Windows NT 下的内核DLL

简介:

   

     再温习<<Windows 2000 Driving>>分层驱动程序一章的时候,看到了关于紧耦合

驱动连接方式,这种方式不依赖于I/O管理器的串联,而是直接调用内核例程,这样可以大

大的提高驱动的执行效率。

     为了实现这样一种功能,必须提供一种类似于在用户模式中DLL的机制,只不过该"DLL"

是加载到内核中的。其实Windows NT 内核本身早就利用了这样机制,比如Hal.dll、ntoskrnl.exe和某些类驱动程序等,他们分别为第3方驱动程序导出了HAL层、内核层、执行体层的功能函数

      在 Tim Roberts 的 《内核模式的 DLL》一文中,给出了内核DLL的主要特征:


    内核 DLL 就像用户模式 DLL 一样:链接器在构建 DLL 时生成一个导入库,然后将此库包含到将要使

用此DLL 的任何驱动程序的目标库列表中。既不需要注册表技巧,也不需要任何特别的动作来起停该 DLL。

内核 DLL将随任何引用之的其他驱动程序自动加载,而随最后一个引用之的驱动程序自动卸载(注 1)

 

      为了实现这样一个DLL,Roberts给出的做法是使用DDK来生成一个TARGETTYPE为

EXPORT_DRIVER的项目我试了一下,如其所愿,的确建立一个xxx.sys文件,我还

观察了一下该sys的PE类型,发现它与标准的内核sys并无两样,那么能不能用gcc来生成一

个内核模式的DLL呢?答案是肯定的!如果你正在用gcc(MinGW)在写Windows NT 下

的Core Dll的话,那么也可以写出DDK中EXPORT_DRIVER类型的模块了,具体做法从略...

 

  好了,上面只是开个玩笑,呵呵。下面就详细说说如何用MinGW来写一个CoreDll:

  1. 首先在你的系统中应该安装一个MinGW,我现在用的是(MinGW 5.1.4);
  2. 在你的系统中还要有一个Masm32V9.0+的环境(这个不是必须的,Gcc也有能力自己生成驱动程序文件,而我采用的是Masm方式),你也可以安装WINDDK,用它的环境生成最终的的驱动文件。
  3. 用gccNTDrvFrame(注2)建立一个新的驱动包,其中如果要想实现CoreDll动态加载和卸载功能必须在sys.c中提供以下2个例程,你可以将构造和析构的相关内容放在它们里面。你同样要提供一个Driver 必须包含标准的 DriverEntry 入口点,不过实际上系统不会调用它。这个需求是创建系统的人为限制,因为它会为每个内核驱动程序把 /ENTRY:DriverEntry 添加到链接器选项中。因此我们为只导出的 DLL 也必须提供一个伪入口点。 :

           __declspec(dllexport) DDKAPI NTSTATUS DllInitialize(/ IN PUNICODE_STRING RegistryPath); __declspec(dllexport) DDKAPI NTSTATUS DllUnload(void);

   4.  建立一个需要导出函数,我这里只是示例一下,所以写了一个简单的导出函数:   

 

__declspec(dllexport) DDKAPI NTSTATUS GetMagicNum(IN int *pVal) { if(!pVal) { DbgPrint("err : pVal == NULL!/n"); return STATUS_DEVICE_CONFIGURATION_ERROR; } DbgPrint("*pVal is %d/n",*pVal); *pVal *= 11 * 11; DbgPrint("After OP --- *pVal is %d/n",*pVal); return STATUS_SUCCESS

 

   5. 为连接器建立def文件,内容如下:

NAME CoreDll.sys EXPORTS DllInitialize PRIVATE DllUnload PRIVATE GetMagicNum

 

    6. 运行gccNTDrvFrame的builder,完成CoreDll的编译和连接,如果没有错误将会生

      成若干个文件,其中只会用到2个:一个是xxx.sys,这个不用说就是供其它驱动调用

      的CoreDll,另一个是xxx.lib,它是所有需要调用xxx.sys的其它驱动程序建立时需要

      的文件,它和hal.lib、ntoskrnl.lib完全一样。

 

   7. 新建另一个Driver工程,在b.bat中的link连接选项中添加xxx.lib。声明和调用外部

       函数的方法如下:

       __declspec(dllimport) DDKAPI NTSTATUS GetMagicNum(IN int *pVal); DDKAPI NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,/ PUNICODE_STRING pRegistryPath) { PDEVICE_OBJECT pDevObj; NTSTATUS status = STATUS_DEVICE_CONFIGURATION_ERROR; PRINT("[%s]enter DriverEntry.../n",__func__); RtlInitUnicodeString(&DevName,DEVNAMEW); RtlInitUnicodeString(&SymlnkName,SYMLNKNAMEW); if(IoCreateDevice(pDriverObject,0,&DevName,FILE_DEVICE_UNKNOWN,/ 0,false,&pDevObj) != STATUS_SUCCESS) { PRINT("[%s]IoCreateDevice Failed!/n",__func__); goto QUIT; } if(IoCreateSymbolicLink(&SymlnkName,&DevName) != STATUS_SUCCESS) { IoDeleteDevice(pDevObj); PRINT("[%s]IoCreateSymbolicLink Failed!/n",/ __func__); goto QUIT; } pDevObj->Flags |= DO_BUFFERED_IO; pDriverObject->DriverUnload = DriverUnload; pDriverObject->MajorFunction[IRP_MJ_CREATE] = / DrvDispatchCreateClose; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = / DrvDispatchCreateClose; pDriverObject->MajorFunction[IRP_MJ_READ] = / DispatchRead; pDriverObject->MajorFunction[IRP_MJ_WRITE] = / DispatchWrite; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = / DrvDispatchControl; pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = / DrvShutdown; if(!NT_SUCCESS(IoRegisterShutdownNotification(/ pDriverObject->DeviceObject))) { PRINT("[%s]err : Register Shutdown Failed!/n",/ __func__); } int i = 11; GetMagicNum(&i); PRINT("[%s]Addr of KeServiceDescriptorTable : %p/n",/ __func__,KeServiceDescriptorTable); PRINT("[%s]leave DriverEntry.../n",__func__); status = STATUS_SUCCESS; QUIT: return status; }

       你当然不一定非要在DriverEntry中调用,因为xxx.sys由系统在调用sys之前自动

       动态加载.

 

         8. 用builder建立这个sys,然后整个过程结束了。

 

   结束语:这里只是一个最简单的示例,如果是只干这样的"小"事而用CoreDll的话,未免

杀鸡用牛刀的意思。CoreDll的用途可以被极大的拓展,至于如何发挥您的激情与才干,就

不是我力所能及的了。(^o^)

 

注1 :不过,在 Windows 98 第二版或者 Windows Me 中内核 DLL 永远也不会卸载。

注2 gccNTDrvFrame是我写的一个gcc建立windows NT 驱动的框架包,稍后会在新文章中介绍。

 

(PS : 文章参考http://sluttery.spaces.live.com/blog/cns!3569FEA80C717FD4!519.entry

 

相关文章
|
5月前
|
存储 Java C语言
Windows 下 JNI 调用动态链接库 dll
Windows 下 JNI 调用动态链接库 dll
147 0
|
5天前
|
Windows
重装系统学习笔记(一):Windows10重装步骤(正版)
这篇博客文章详细介绍了如何使用微软官方工具重装Windows 10系统,包括下载安装工具、准备U盘、设置BIOS以及系统安装的步骤。
11 0
重装系统学习笔记(一):Windows10重装步骤(正版)
|
1月前
|
Docker Windows Python
Windows-Docker学习笔记(一)
本文介绍了Windows环境下Docker的安装与使用方法。首先通过链接下载Windows版本的Docker,然后按步骤配置:启用Windows功能、设置默认WSL版本为2,并检查Docker及其组件是否正常工作。接着详细列出了Docker的基础命令及其用法,包括搜索、下载镜像、管理容器等。同时介绍了Dockerfile的编写方法及其构建过程,以及docker-compose.yml文件的用途。最后提供了运行和管理容器的具体示例。
Windows-Docker学习笔记(一)
|
2月前
|
XML C# 数据格式
绝密档案曝光!Windows平台如何深挖一个dll背后的神秘依赖,揭露隐藏的秘密!
【8月更文挑战第14天】在Windows系统中,动态链接库(DLL)对程序运行至关重要。了解DLL的依赖关系有助于软件的调试与优化。本文以具体案例演示如何查看DLL依赖。首先确保环境已安装Windows及具备基本开发知识。
50 0
|
4月前
|
Windows
【Windows内核驱动函数(1)】IoCreateSymbolicLink()-----创建符号链接函数
【Windows内核驱动函数(1)】IoCreateSymbolicLink()-----创建符号链接函数
|
5月前
|
存储 Java C++
Windows 下 JNA 调用动态链接库 dll
Windows 下 JNA 调用动态链接库 dll
111 0
|
5月前
|
网络协议 Shell vr&ar
某教程学习笔记(一):1、windows基础
某教程学习笔记(一):1、windows基础
32 0
|
10月前
|
Windows
5.4 Windows驱动开发:内核通过PEB取进程参数
PEB结构`(Process Envirorment Block Structure)`其中文名是进程环境块信息,进程环境块内部包含了进程运行的详细参数信息,每一个进程在运行后都会存在一个特有的PEB结构,通过附加进程并遍历这段结构即可得到非常多的有用信息。在应用层下,如果想要得到PEB的基地址只需要取`fs:[0x30]`即可,TEB线程环境块则是`fs:[0x18]`,如果在内核层想要得到应用层进程的PEB信息我们需要调用特定的内核函数来获取。
5.4 Windows驱动开发:内核通过PEB取进程参数
|
10月前
|
API Windows
5.3 Windows驱动开发:内核取应用层模块基址
在上一篇文章`《内核取ntoskrnl模块基地址》`中我们通过调用内核API函数获取到了内核进程`ntoskrnl.exe`的基址,当在某些场景中,我们不仅需要得到内核的基地址,也需要得到特定进程内某个模块的基地址,显然上篇文章中的方法是做不到的,本篇文章将实现内核层读取32位应用层中特定进程模块基址功能。
5.3 Windows驱动开发:内核取应用层模块基址
|
10月前
|
存储 安全 API
5.2 Windows驱动开发:内核取KERNEL模块基址
模块是程序加载时被动态装载的,模块在装载后其存在于内存中同样存在一个内存基址,当我们需要操作这个模块时,通常第一步就是要得到该模块的内存基址,模块分为用户模块和内核模块,这里的用户模块指的是应用层进程运行后加载的模块,内核模块指的是内核中特定模块地址,本篇文章将实现一个获取驱动`ntoskrnl.exe`的基地址以及长度,此功能是驱动开发中尤其是安全软件开发中必不可少的一个功能。
5.2 Windows驱动开发:内核取KERNEL模块基址