天天看点

[原创]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:

首先在你的系统中应该安装一个mingw,我现在用的是(mingw 5.1.4);

在你的系统中还要有一个masm32v9.0+的环境(这个不是必须的,gcc也有能力自己生成驱动程序文件,而我采用的是masm方式),你也可以安装winddk,用它的环境生成最终的的驱动文件。

用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__);

pdevobj->flags |= do_buffered_io;

pdriverobject->driverunload = driverunload;

pdriverobject->majorfunction[irp_mj_create] = /

drvdispatchcreateclose;

pdriverobject->majorfunction[irp_mj_close] = /

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",/

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 驱动的框架包,稍后会在新文章中介绍。

继续阅读