dunniu
驱动老牛
驱动老牛
  • 注册日期2005-05-26
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分555分
  • 威望0点
  • 贡献值0点
  • 好评度997点
  • 原创分0分
  • 专家分0分
阅读:1154回复:0

Introduction to Drivers

楼主#
更多 发布于:2005-07-29 13:12
  Introduction
This tutorial will attempt to describe how to write a simple device driver for Windows NT. There are various resources and tutorials on the internet for writing device drivers, however, they are somewhat scarce as compared to writing a “hello world” GUI program for Windows. This makes the search for information on starting to write device drivers a bit harder. You may think that if there’s already one tutorial, why do you need more? The answer is that more information is always better especially when you are first beginning to understand a concept. It is always good to see information from different perspectives. People write differently and describe certain pieces of information in a different light depending on how familiar they are with a certain aspect or how they think it should be explained. This being the case, I would recommend anyone who wants to write device drivers not to stop here or somewhere else. Always find a variety of samples and code snippets and research the differences. Sometimes there are bugs and things omitted. Sometimes there are things that are being done that aren’t necessary, and sometimes there’s information incorrect or just incomplete.
This tutorial will describe how to create a simple device driver, dynamically load and unload it, and finally talk to it from user mode.
Creating a Simple Device Driver
What is a subsystem?
I need to define a starting ground before we begin to explain how to write a device driver. The starting point for this article will be the compiler. The compiler and linker generate a binary in a format that the Operating System understands. In Windows, this format is “PE” for “Portable Executable” format. In this format, there is an idea called a subsystem. A subsystem, along with other options specified in the PE header information, describes how to load an executable which also includes the entry point into the binary.
Many people use the VC++ IDE to simply create a project with some default pre-set options for the compiler’s (and linker) command line. This is why a lot of people may not be familiar with this concept even though they are most likely already using it if they have ever written Windows applications. Have you ever written a console application? Have you ever written a GUI application for Windows? These are different subsystems in Windows. Both of these will generate a PE binary with the appropriate subsystem information. This is also why a console application uses “main” where a WINDOWS application uses “WinMain”. When you choose these projects, VC++ simply creates a project with /SUBSYSTEM:CONSOLE or /SUBSYSTEM:WINDOWS. If you accidentally choose the wrong project, you can simply change this in the linker options menu rather than needing to create a new project.
There’s a point to all of this? A driver is simply linked using a different subsystem called “NATIVE”. MSDN Subsystem compiler options.
The Driver’s “main”
After the compiler is setup with the appropriate options, it’s probably good to start thinking about the entry point to a driver. The first section lied a little bit about the subsystem. “NATIVE” can also be used to run user-mode applications which define an entry point called “NtProcessStartup”. This is the “default” type of executable that is made when specifying “NATIVE” in the same way “WinMain” and “main” are found when the linker is creating an application. You can override the default entry point with your own, simply by using the “-entry:<functionname>” linker option. If we know we want this to be a driver, we simply need to write an entry point whose parameter list and return type matches that of a driver. The system will then load the driver when we install it and tell the system that it is a driver.
The name we use can be anything. We can call it BufferFly() if we want. The most common practice used by driver developers and Microsoft is using the name “DriverEntry” as its initial entry point. This means we add “-entry:DriverEntry” to the linker’s command line options. If you are using the DDK, this is done for you when you specify “DRIVER” as the type of executable to build. The DDK contains an environment that has pre-set options in the common make file directory which makes it simpler to create an application as it specifies the default options. The actual driver developer can then override these settings in the make file or simply use them as a connivance. This is essentially how “DriverEntry” became the somewhat “official” name for driver entry points.
Remember, DLLs actually are also compiled specifying “WINDOWS” as the subsystem, but they also have an additional switch called /DLL. There is a switch which can also be used for drivers: /DRIVER:WDM (which also sets NATIVE behind the scenes) as well as a /DRIVER:UP which means this driver cannot be loaded on a multi-processor system.
The linker builds the final binary, and based on what the options are in the PE header and how the binary is attempting to be loaded (run as an EXE through the loader, loaded by LoadLibrary, or attempting to be loaded as a driver) will define how the loading system behaves. The loading system attempts to perform some level of verification, that the image being loaded is indeed supposed to be loaded in this manner, for example. There is even, in some cases, startup code added to the binary that executes before your entry point is reached (WinMainCRTStartup calling WinMain, for example, to initialize the CRT). Your job is to simply write the application based on how you want it to be loaded and then set the correct options in the linker so it knows how to properly create the binary. There are various resources on the details of the PE format which you should be able to find if you are interested in further investigation into this area.
The options we will set for the linker will end up being the following:
/SUBSYSTEM:NATIVE /DRIVER:WDM
游客

返回顶部