youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:1674回复:12

请高手过来看看这个两个驱动通信的问题

楼主#
更多 发布于:2004-05-01 21:15
这里有一段胡老大关于两个驱动通信的发言:
“我再在这里给大家提供一个非正规,但是却颇有趣的方法。
你编译驱动程序的时候,让sys生成一个lib,输出一个函数,
于是两个driver之间就可以互相调用,不要说传递一个event,
传递什么东西都可以了。”

我有如下问题:
1.如何让SYS生成LIB;
2.如何让一个SYS可以有输出函数?
3.假设让一个SYS生成了LIB也有了输出函数,那么另一个又如何调用呢?

大佬们的说话太简洁了,搞得我们这些后生摸不着头脑啊,希望这里还在的大佬们帮帮忙,说得清楚点好吗?

谢谢了!!!!

arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
沙发#
发布于:2004-05-01 23:24
driver to driver通讯,有一个方法,基于“driver都在一个内存空间”的原理。

driver A和driver B可以共享一块区域,这个区域你可以放数据,函数指针,可以是A创建,初始化,然后A可以用IRP_MJ_INTERNAL_ DEVICE_CONTROL,构建一个irp, IoCallDriver把区域的地址给driver B,在Irp->UserBuffer
当然,要考虑一下同步的问题。


想输出函数,
1、可以用def文件,和win32的一样
2、__declspec(dllexport)
自然会有lib,有lib了,就可以调用了
想详细一点,可以到 [url] http://www.wd-3.com/[/url]看看,有文档讲了的。
youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2004-05-02 11:43
多谢你的回答,先送上20分。我还有点问题:

1.我的本意其实不是想两个驱动间调来调去,我是想一个驱动动态调用一个DLL,但是用VC工程编出来的那种DLL在核心层能挂上去吗?有个大佬说核心层态的DLL可以,但如何编译出核心太的DLL?我昨天无意中发现Driver的notify object就是一个DLL,还随着驱动安装时加载了,不知道这个DLL是否就是核心态的DLL呢?

2.有无这方面完整一点的代码例程呢?

再次感谢。
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
地板#
发布于:2004-05-02 12:14
kernel的dll和user-mode的dll其实一样用。

1、好像不是的,也是ring3的dll
2、http://www.wd-3.com/有呀,呵呵

DLLs in Kernel Mode
July 15, 2003
Tim Roberts
Copyright ? 2003, Tim Roberts. All rights reserved

Win32 user-mode programmers are accustomed to using and creating dynamic link libraries, or DLLs, to compartmentalize their applications and enable efficient code reuse. The typical application includes many DLLs, and careful design allows those DLLs to be reused many times over.

Kernel driver writers are often not aware that they can use exactly the same concept in kernel mode. The standard DDK even includes several samples (for example, storage/changers/class). In this article, I will show you a working (though trivial) example of a kernel DLL.


The Basics
In terms of the C source code, a kernel DLL is virtually identical to a user-mode DLL. The primary difference is that you may not call any user-mode APIs from a kernel DLL. That should not be surprising.

You use a kernel DLL just like a user-mode DLL: the linker builds an import library when it builds your DLL, and you include that library in the target library list for any driver that needs to use the DLL. No registry magic is required, and no special action is needed to start or stop the DLL. Your kernel DLL will be automatically loaded as soon as any other driver makes a reference to it, and is automatically unloaded when the last referring driver unloads.1

You can export DLL entry points from a normal WDM driver as well. There are many drivers in the operating system that export entry points for other drivers to use. For example, the ubiquitous NTOSKRNL.EXE, which contains all of the Ex, Fs, Io, Ke, Mm, Nt, and Zw entry points used by virtually every driver, is nothing more than a standard kernel driver with exports, exactly like the DLL we will describe here.


Digging In
OK, now let\'s get in to a few of the details. All of the source files for this project are available at http://www.wd-3.com/downloads/kdll.zip.

The most important step to take when creating an export driver is to specify the TARGETTYPE macro in the \"sources\" file:

TARGETTYPE=EXPORT_DRIVER
This type tells the build system that our project will build a kernel-mode driver that is exporting functions. If you leave TARGETTYPE set to DRIVER, as with a normal kernel-mode driver, your exports will not be available to other drivers.

Your DLL must include the standard DriverEntry entry point, but the system won\'t actually call that entry point. This requirement is an artifact of the build system, which adds /ENTRY:DriverEntry to the linker options for every kernel driver. An EXPORT_DRIVER can also function as a normal driver, and the build system cannot tell whether we want to do that or not, so we have to supply this dummy entry point for an export-only DLL.

If you do need to take special one-time action on loading and unloading, you should export two special enty points called DllInitialize and DllUnload:

NTSTATUS DllInitialize(IN PUNICODE_STRING RegistryPath)
  {
  DbgPrint(\"SAMPLE: DllInitialize(%wZ)\\n\", RegistryPath);
  return STATUS_SUCCESS;
  }

NTSTATUS DllUnload()
  {
  DbgPrint(\"SAMPLE: DllUnload\\n\");
  return STATUS_SUCCESS;
  }
The RegistryPath string passed to DllInitialize is of the form:

\\Registry\\Machine\\System\\CurrentControlSet\\Services\\SAMPLE

You would only include a DllInitialize routine in an export-only DLL -- that is, in a driver that\'s used exclusively as a DLL and not as a real driver for hardware. You do not need to define a service key for such a driver. Consequently, the RegistryPath string is not probably not going to be useful to you, because it likely names a registry key that doesn\'t even exist.

Compatibility caution: A bug in Windows 98 Gold will prevent your DLL from loading if you include a DllInitialize entry point. Further, Windows 98 Second Edition and Windows Millennium will never call the DllUnload entry point. A kernel DLL in these systems, once loaded, is permanent.


Declaring Exports
Beyond those two special entry points, you can create whatever entry point names you find convenient. You just need to identify those entry point names to the linker. There are two ways to do that. For our sample purposes, I will be exporting one functional entry point from our DLL:

NTSTATUS SampleDouble(int* pValue)
  {
  DbgPrint(\"SampleDouble: %d\\n\", *pValue);
  *pValue *= 2;
  return STATUS_SUCCESS;
  }
There are two ways to tell the linker that you want to export a function. The first method is to enumerate the names in a .DEF file. The .DEF file is familiar to anyone who has done Win16 or Win32 programming. It is a special file used to give instructions to the linker that cannot be easily included on the command line. In this case, it enumerates the names of the routines we want to export from the DLL. The linker uses this list to create the symbol tables in the DLL, and to create an import library that we can use in other projects to call into our DLL. Our .DEF file looks like this:

NAME SAMPLE.SYS

EXPORTS
  DllInitialize PRIVATE
  DllUnload PRIVATE
  SampleDouble
DllInitialize and DllUnload must both be marked as PRIVATE. This tells the linker to export the symbol from the DLL executable file, but not to include it in the import library it builds. The build system will flag an error if these are not marked PRIVATE.

The import library is the fundamental mechanism used to map a function\'s name to the DLL that contains that function. Almost all of the libraries you use in Win32 program are import libraries, including kernel libraries such as ntdll.lib and ntoskrnl.lib, and user-mode libraries such as kernel32.lib, user32.lib, and gdi32.lib. Such libraries do not actually contain any code. Instead, they contain a set of linker tables that contains information that means something like, \"The name MySampleFunction maps to _MySampleFunction@4 in MY.DLL\".

The linker embeds this information into the executable file, so that the operating system can tie all of the loose ends together when the EXE or DLL is finally loaded into memory.

We have to use the special DLLDEF macro in the \"sources\" file to identify the name of our .DEF file:

DLLDEF=sample.def
The second way to identify your exported entry points is to use a declspec attribute in your source code:

__declspec(dllexport) NTSTATUS SampleDouble(int* pValue)
  {
  ...
  }
This has the same effect as listing the name in the .DEF file. In general, I am in favor of reducing the number of files in my project, since that automatically reduces the number of chances for error. However, in this case, there is a catch: DllInitialize and DllUnload must be marked as PRIVATE exports, and to my knowledge, there is no way to mark an export PRIVATE without using a .DEF file. Thus, you will HAVE to use the .DEF file at least for those two names. Whether you include your other exports in the .DEF file or mark them with __declspec(dllexport) is completely up to you.

The sample source code for this article is in C. If you wish to export functions from a DLL written in C++, you have an additional complication to consider. Because C++ allows multiple functions with the same name but different argument lists, C++ compilers \"decorate\" their symbol names with extra characters that specifically identify the return type and argument list. For example, the actual name of the SampleDouble function when compiled in a C++ module is ?SampleDouble@@YGJPAH@Z. If you try to call this function from another C++ driver, it would work, but if you try to call it from a C driver, the external names won\'t line up.

The way to fix this is to use a special language modifier on the extern declaration, like this:

extern \"C\" NTSTATUS SampleDouble(int* pValue)
  {
  ...
  }
With that information, we can now bring up a DDK command shell and do a build. For this example, we build a file called sample.sys. We copy this file to the traditional location for drivers, %WINDIR%\\SYSTEM32\\DRIVERS, and we are ready to use our DLL. We can verify the exports using the \"dumpbin\" command, just like you would with a user-mode DLL:

C:\\Dev\\KernDLL>dumpbin /exports objfre_w2k_x86\\i386\\sample.sys

Microsoft (R) COFF/PE Dumper Version 7.00.9210
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file objfre_w2k_x86\\i386\\sample.sys

File Type: EXECUTABLE IMAGE

  Section contains the following exports for SAMPLE.SYS

  00000000 characteristics
  3EEEB656 time date stamp Mon Jun 16 23:33:58 2003
  0.00 version
    1 ordinal base
    3 number of functions
    3 number of names
        
  ordinal hint RVA      name
        
    1    0 0000031B DllInitialize
    2    1 00000347 DllUnload
    3    2 00000368 SampleDouble
    ...
Alternatively, you can use the Dependency Walker applet (DEPENDS.EXE) from the Platform SDK to view the file:



Notice that the Subsystem displayed for all the modules in the bottom pane is \"Native\". If you ever see a \"Win32\" subsystem when you\'re looking at the dependencies for a driver or kernel DLL you\'ve created, it means that you\'ve called a user-mode API function.

Calling Into the DLL
To make it convenient to call the entry points in our DLL, we probably want to create a header file to include in our calling driver. For this sample, we can use this simple sample.h:

#pragma once
EXTERN_C DECLSPEC_IMPORT NTSTATUS SampleDouble(int* pValue);
We use several custom macros here to make the file flexible and easier to read. These macros are defined in <ntdef.h>, which is included automatically in most drivers via <wdm.h>

EXTERN_C expands to extern \"C\" in a C++ source file, and to plain old extern in a C source file. This ensures that no unwanted decoration will occur in the calling program.

DECLSPEC_IMPORT expands to the Visual C++ specifier __declspec(dllimport). This is the complement of the __declspec(dllexport) macro we used above, and tells the compiler that the call will be satisfied from a DLL at run-time, rather than being loaded at link-time. This allows the compiler and the linker to optimize the runtime linkage to SampleDouble.2

When you define a header file like this that contains function prototypes for a DLL, it\'s a good idea to include that header in the DLL project too. Doing that gives you a compile-time check that you\'ve correctly prototyped the functions. All those DECLSPEC_IMPORT directives will cause warning messages from the compiler, however. You can cure that minor problem by putting some conditional compilation into the header:

#pragma once
#ifdef SAMPLE_INTERNAL
  #define SAMPLE_IMPORT
#else
  #define SAMPLE_IMPORT DECLSPEC_IMPORT
#endif

EXTERN_C SAMPLE_IMPORT SampleDouble(int* pValue);

You define the symbol SAMPLE_INTERNAL in your DLL project, which causes the header to not have any __declspec directives. Other projects that include the header don\'t define this symbol, which means that the header does contain the directives.

Testing
To test our sample, I added the following code to the DriverEntry of a kernel driver I have been working on:

#include \"sample.h\"

NTSTATUS DriverEntry(...)
  {
  PDEVICE_OBJECT deviceObject = NULL;
  NTSTATUS       ntStatus;
  WCHAR          deviceNameBuffer[] = L\"\\\\Device\\\\dbgdrvr\";
  UNICODE_STRING deviceNameUnicodeString;
  WCHAR          deviceLinkBuffer[] = L\"\\\\DosDevices\\\\DBGDRVR\";
  UNICODE_STRING deviceLinkUnicodeString;
  int xxx = 19;
 
  KdPrint((\"HELPER.SYS: entering DriverEntry\\n\"));
  KdPrint ((\"Helper: before is %d\\n\", xxx));
  SampleDouble(&xxx);
  KdPrint((\"Helper:  after is %d\\n\", xxx));
  ...
  }

I copied \"sample.lib\" from my sample build directory into the test build directory, and added \"sample.lib\" to the TARGETLIBS macro in \"sources\". In fact, because my test driver is so simple, the entire sources file is here:

TARGETNAME=dbgdrvr
TARGETPATH=obj
TARGETTYPE=DRIVER

TARGETLIBS=sample.lib

SOURCES=dbgdrvr.c

I then built my driver and copied the binary to SYSTEM32\\DRIVERS. This is an old-style NT 4 driver, so I started it up with \"net start\" and stopped it with \"net stop\". The resulting debug log looked like this:

SAMPLE: DllInitialize(\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\SERVICES\\SAMPLE)
HELPER.SYS: entering DriverEntry
Helper: before is 19
SampleDouble: 19
Helper:  after is 38
HELPER.SYS: unloading
SAMPLE: DllUnload

Note that our DLL loads before the calling driver starts to execute, and unloads after the calling driver shuts down. This, again, is similar to the way a Win32 user-mode DLL operates: the system does not know whether we intend to call the DLL within our DriverEntry or not, so it ensures that all DLLs are in place an initialized before launching the referring driver.

You can see the registry path being passed to the DllInitialize entry point of my kernel DLL. You\'ll have to trust me when I tell you there is no such path in my registry; the string is just for decoration.


Conclusion
This is a lot of work just to double an integer, but it demonstrates a powerful and little-known concept. With a little forethought, you can build a centralized repository for all of your interesting overhead routines, hiding the sometimes daunting complexity of the kernel APIs in a simple wrapper that you can use over and over again.


About the author:
Tim Roberts is a hopeless software engineer who programs both for fun and for profit. Tim has been programming computers for more than a third of a century, on everything from microcontrollers to mainframes.

Tim is a partner in Providenza & Boekelheide, Inc., a technology consulting company in the Silicon Forest just outside of Portland, Oregon. P&B provides all kinds of hardware and software consulting, specializing in graphics, video, and multimedia.


--------------------------------------------------------------------------------

1 -- Kernel DLLS are never unloaded in Windows 98 Second Edition or in Windows Millennium, though. I\'ll say more about platform compatibility later on in the article.

2 -- If you give the compiler the clue that a given function will be imported from another DLL, it generates an indirect call through the module\'s indirect address table. If you don\'t give the compiler this clue, it generates a call to an external function. The linker then includes a function thunk (taken from the import library) that contains an indirect call through the indirect address table. Thus, using __declspec(dllimport) eliminates the thunk in the middle and saves a few machine cycles at run time.





 

[编辑 -  5/2/04 by  arthurtu]
youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2004-05-02 12:51
O,YES,请原谅我的不仔细,我在网上找到了。
等我看完了就给加上另30分 :D :D

BTW:这个网上的文章好像不多,是不是我没找到啊?我只看到Archieve里有些文章,那这个WD3的过刊在哪里查看呢?
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
5楼#
发布于:2004-05-02 12:55
确实不多的
youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
6楼#
发布于:2004-05-02 13:40
看完了,请指出我下面话的错误:

1.编译出来的sample.SYS文件(带输出的)拷贝到系统Drivers目录下,然后在我的TEST驱动目录下拷那个sample.lib过来,再在SOURCES中加入这个LIB。紧接着将我的驱动TEST.sys拷到系统DRIVERS目录下,启动我的驱动,这样我的TEST驱动就可以自己将Sample.sys装载(它能自己找到SAMPLE.SYS吗?根据什么线索啊,sample.lib??)。

2.sample.sys是动态加载吗?这种在编译时就加LIB的方法,在应用层编程时好像是静态链接吧,动态的我总觉得是在驱动调用一些语句动态加载。

youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
7楼#
发布于:2004-05-02 13:55
唉呀,给过一次分就不能再给了???
老大,没办法,我只有新开贴给你那30分了。
youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
8楼#
发布于:2004-05-02 17:05
我做了实验,我做的是IMDRIVER(PASSTHRU),在安装驱动之前会加载那个sample.sys,而且中间替换了sample.sys也没有用,要重新安装PASSTHRU才能生效。这能不能说明这种方法并不是动态加载的呢?

有没有什么办法可以动态加载的呢?
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
9楼#
发布于:2004-05-02 20:05
应该是在load你的sys的时候,把你用的(import)sys也load了,怎么是“在安装驱动之前会加载那个sample.sys”?在load你的sys的时候会检查的,和user mode一样
动态加载,好像LoadLibrary一样的?
似乎要用native api吧,ZwSetSystemInformation的SystemLoadAndCallImage或SystemLoadImage,可以试一下,ZwLoadDriver可能还不适合。

btw,我可不够资格叫老大,拜托,会被别人笑话的 :o

[编辑 -  5/2/04 by  arthurtu]
youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
10楼#
发布于:2004-05-02 22:45
多谢你提出的思路,这个函数在DDK和MSDN里没介绍吧,我找了一篇如何用这个函数加载ROOTKIT的文章,但没怎么看懂。又要多嘴问一句你有没有例子代码啦 ;)(有NATIVE API那本电子版 吗?)

BTW,我开了一个新贴给你加后面的30分,你回复一下。
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
11楼#
发布于:2004-05-02 22:57
2k native的电子文档,站上有下载的呀,sample code也有的
youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
12楼#
发布于:2004-05-02 23:02
我看这个两驱动间通信(静态)问题就算结贴了吧!

关于这个动态加载的问题我再开个新贴讨论,希望你继续参与!
 :D :D :D :D
游客

返回顶部