Driver: Hello World

Source: Internet
Author: User
Tags knowledge base

We learned Program Design from "Hello World" and the driver is no exception. Today I wrote a "hello World" driver version to warm up, I hope you can understand the basic framework of the driver.

Drivers are divided into two categories: one is the kernel mode driver and the other is the Windows mode driver. The two models are essentially the same, but the details are different, this article describes how to install and use kernel-mode drivers and drivers.

Drivers, like normal EXE and DLL, all belong to PE files and all have an entry function. But in EXE, the entry function is main ()/winmain () and Unicode wmain ()/wwinmain (). The DLL entry function is dispensable, And it is dllmain (). The driver also has an entry function and is required. It is DriverEntry (), and it is required again, because the I/O manager will first call the driver's DriverEntry (), it acts like dllmain ()-to complete some initialization work. DriverEntry () has two parameters: 1pdriver_object driverobject, which points to the pointer to the driver object. we operate the driver and rely on it. It is passed in by the I/O manager. 2) punicode_string registrypath is the service primary key of the driver. this parameter is not used many times, but it may disappear after DriverEntry () is returned, remember to save it first. DriverEntry () returns an ntstatus value, which is a ulong value. For specific definitions, see the ntstatus. h header file in DDK, which contains detailed definitions.

To write the "Hello World" of the driver version, determine how to communicate with the driver, common shared memory, shared events, IOCTL macros, or directly use readfile () or writefile () for read/write. In this article, I will use a simple but very common IOCTL macro, which relies on irp_mj_device_control, the Win32 program uses deviceiocontrol () to communicate with the driver, and outputs different debugging information based on different IOCTL macros. For ease of use, I did not use readfile () to read the information, but directly output the data using dbgprs int (). Therefore, you need to use dbgview to view the information. Other debugging tools are also supported. PS: lazy!

The driver communicates with the I/O manager and uses IRP, that is, the I/O Request package. IRP is divided into two parts: 1) IRP header; 2) IRP stack. The IRP header information is as follows:

IRP header:

Io_status_block iostatus contains the I/O Request status
Pvoid associatedirp. systembuffer if the buffer I/O is executed, this pointer points to the system buffer.
Pmdl mdladdress if direct I/O, this pointer points to the memory Descriptor Table of the user buffer zone
User space address of pvoid userbuffer I/O Buffer

IRP Stack:

Uchar majorfunction indicates the irp_mj_xxx dispatch routine
Uchar minorfunction is the same as above. It is used by common file systems and scsi drivers.
 
Union parameters majorfunction union type
{
Struct read irp_mj_read Parameter
Ulong Length
Ulong key
Large_integer byteoffset
 
Struct write irp_mj_write Parameter
Ulong Length
Ulong key
Large_integer byteoffset
 
Struct deviceiocontrol irp_mj_device_control Parameter
Ulong outputbufferlength
Ulong inputbufferlength
Ulong iocontrolcode
Pvoid type3inputbuffer
}
Pointer to the target device object requested by pdevice_object deviceobject
Pointer to the target file object requested by pfile_object fileobject, if any

Operation IRP. Different IRP functions have different operations: Some operate only the IRP header, some operate only the IRP stack, and others operate on the IRP whole. Below are some common functions:

IRP overall:

Name Description caller
Iostartpacket sends IRP to start I/O routine dispatch
Iocompleterequest indicates that all processing is completed.
Iostartnextpacket sends the next IRP to start I/O routine dpcforisr
Iocalldriver sends IRP Request dispatch
Ioallocateirp requests another IRP dispatch
Iofreeirp releases the irp I/O completion allocated by the driver.

IRP Stack:

Name Description caller
Iogetcurrentirpstacklocation to get the caller's stack pointer dispatch
Iomarkirppending marks the caller I/O stack dispatch for further processing
Iogetnextirpstacklocation to get the pointer dispatch of the I/O stack of the next driver
Iosetnextirpstacklocation pushes the I/O Stack pointer to the stack dispatc

In the driver program, IRP dispatch routines play an important role. Almost all IRP dispatch routines have corresponding Win32 functions. Below are several common ones:

IRP dispatch routine:

Name Description caller
Irp_mj_create requests a handle createfile
Irp_mj_cleanup cancels the hanging IRP closehandle when the handle is closed.
Irp_mj_close close handle closehandle
Irp_mj_read get data from the device readfile
Irp_mj_write transfers data to the device writefile
Irp_mj_device_control operation (using IOCTL macro) deviceiocontrol
Irp_mj_internal_device_control (can only be called by the kernel) N/
Irp_mj_query_information: Get the object length getfilesize.
Irp_mj_set_information sets the object length setfilesize
Irp_mj_flush_buffers: Write the output buffer or discard the flushfilebuffers flushconsoleinputbuffer purgecomm.
Irp_mj_shutdown: initiatesystemshutdown is disabled.

========================================================== ========================================================== ================================

Next we will start to write the "Hello World" for our driver version. The program is very simple. Let's first introduce the process:

1. Call iocreatedevice () to create a device and return a device object.
2. Call iocreatesynboliclink () to create a symbolic connection so that Win32 programs can use the driver.
3. Set the irp_mj_device_control dispatch routine helloworlddispatch () and uninstall routine helloworldunload ().

If the Win32 program uses deviceiocontrol (), the helloworlddispatch () function is executed.
4. Call iogetcurrentirpstacklocation () to obtain the IRP pointer of the current caller.
5. Obtain the IO control code and use iocompleterequest () to complete the IRP operation.

If you use controlservice () to stop the driver, execute the helloworldunload () function.
4. Call iodeletesymboliclink () to delete the symbolic connection.
5. Call iodeletedevice () to delete the created device.

Driver entry DriverEntry ()

// Create a device
Iocreatedevice (driverobject, // driver object
0, // expand the size of the device. Set this parameter to 0 because it is not required.
& Devicenamestring, // device name
File_device_unknown, // device type
0, // indicates the operation permitted by the device
False, // if it is true, it indicates that only one thread can use this device. If it is false, there is no limit.
& Lpdeviceobject); // The returned device object.

// Create a symbolic connection
Iocreatesymboliclink (& devicelinkstring, // unicode_string that stores the symbolic connection
& Devicenamestring); // device name

// Dispatch and uninstall routines
Driverobject-> majorfunction [irp_mj_device_control] = helloworlddispatch;
Driverobject-> driverunload = helloworldunload;

IRP dispatch routine helloworlddispatch ()

Irpstack = iogetcurrentirpstacklocation (pirp); // obtain the IRP stack of the current caller

// Obtain the IO control code and execute the specified operation. Here, it is only dbuplint ()
Iocontrolcodes = irpstack-> parameters. deviceiocontrol. iocontrolcode;
Switch (iocontrolcodes ){
......

Iocompleterequest (pirp, io_no_increment); // complete the IRP operation.

Uninstall routine helloworldunload ()

// Delete the symbolic connection and Device
Iodeletesymboliclink (& devicelinkstring );
Iodeletedevice (driverobject-> deviceobject );

========================================================== ========================================================== =============================== Complete code:

# Ifndef _ helloworld_c __
# DEFINE _ helloworld_c __

# Define debugmsg

# Include "helloworld. H"

// Driver entry
Ntstatus DriverEntry (in pdriver_object driverobject, in punicode_string registrypath)
{
Ntstatus = STATUS_SUCCESS;
Pdevice_object lpdeviceobject = NULL; // pointer to the device object
Unicode_string devicenamestring; // device name
Unicode_string devicelinkstring; // symbolic connection

// Debug information
# Ifdef debugmsg
Dbuplint ("Starting DriverEntry ()/n ");
# Endif

Rtlinitunicodestring (& devicenamestring, nt_device_name); // initialize the Unicode string

// Create a device
Ntstatus = iocreatedevice (driverobject, 0, & devicenamestring, file_device_unknown,
0, false, & lpdeviceobject );

// Use the nt_success macro to check whether the function call is successful
If (! Nt_success (ntstatus ))
{
# Ifdef debugmsg
Dbuplint ("error iocreatedevice ()/n ");
# Endif
Goto error;
}

Rtlinitunicodestring (& devicelinkstring, dos_device_name );

// Create a symbolic connection
Ntstatus = iocreatesymboliclink (& devicelinkstring, & devicenamestring );

If (! Nt_success (ntstatus ))
{
# Ifdef debugmsg
Dbuplint ("error iocreatesymboliclink ()/n ");
# Endif
Goto error;
}

// Set the IRP dispatch routine and unmount routine
Driverobject-> majorfunction [irp_mj_create] = // helloworlddispatch;
Driverobject-> majorfunction [irp_mj_close] = // helloworlddispatch;
Driverobject-> majorfunction [irp_mj_device_control] = helloworlddispatch;
Driverobject-> driverunload = helloworldunload;

Return ntstatus;

Error:
# Ifdef debugmsg
Dbuplint ("error DriverEntry ()/n ");
# Endif

Return ntstatus;
}

Ntstatus helloworlddispatch (in pdevice_object deviceobject, in pirp)
{
Ntstatus = STATUS_SUCCESS;
Ulong iocontrolcodes = 0; // I/O control code
Pio_stack_location irpstack = NULL; // IRP Stack

// Sets the IRP status.
Pirp-> iostatus. Status = STATUS_SUCCESS;
Pirp-> iostatus. Information = 0;

# Ifdef debugmsg
Dbuplint ("Starting helloworlddispatch ()/n ");
# Endif

Irpstack = iogetcurrentirpstacklocation (pirp); // obtain the IRP of the current caller

Switch (irpstack-> majorfunction)
{
Case irp_mj_create:
# Ifdef debugmsg
Dbuplint ("irp_mj_create/N ");
# Endif
Break;

Case irp_mj_close:
# Ifdef debugmsg
Dbuplint ("irp_mj_close/N ");
# Endif
Break;

Case irp_mj_device_control:

# Ifdef debugmsg
Dbuplint ("irp_mj_device_control/N ");
# Endif

// Obtain the I/O control code
Iocontrolcodes = irpstack-> parameters. deviceiocontrol. iocontrolcode;

Switch (iocontrolcodes)
{
// Start
Case start_hellpworld:
Dbuplint ("starting/" Hello World/"/N ");
Break;

// Stop
Case stop_hellpworld:
Dbuplint ("stoping/" Hello World/"/N ");
Break;

Default:
Pirp-> iostatus. Status = status_invalid_parameter;
Break;
}

Break;

Default: break;
}

Ntstatus = pir-> iostatus. status;

Iocompleterequest (pirp, io_no_increment );

Return ntstatus;
}

Void helloworldunload (in pdriver_object driverobject)
{
Unicode_string devicelinkstring;
Pdevice_object deviceobjecttemp1 = NULL;
Pdevice_object deviceobjecttemp2 = NULL;

# Ifdef debugmsg
Dbuplint ("Starting helloworldunload ()/n ");
# Endif

Rtlinitunicodestring (& devicelinkstring, dos_device_name );
Iodeletesymboliclink (& devicelinkstring); // delete a symbolic connection

If (driverobject)
{
Deviceobjecttemp1 = driverobject-> deviceobject;

// Delete a device
While (deviceobjecttemp1)
{
Deviceobjecttemp2 = deviceobjecttemp1;
Deviceobjecttemp1 = deviceobjecttemp1-> nextdevice;
Iodeletedevice (deviceobjecttemp2 );
}
}
}

# Endif

========================================================== ========================================================== ================================
To compile the driver, you must use the build utility in DDK. It is a command line program and is not very convenient to use. The VC knowledge base has an article on compiling the driver in VC ++ 6.0. If you are interested, go and have a look.

1. makefile
To compile the driver, you must first prepare a makefile, which is very simple and has only one code:
#
# Do not edit this file !!! Edit./sources. If you want to add a new source
# File to this component. This file merely indirects to the real make File
# That is shared by all the driver components of the Windows NT DDK
#

! Include $ (ntmakeenv)/makefile. Def

As described, do not modify this file-it is common!

2, sources
The second file to be prepared is sources, which describes some compilation details. For the program in this article, the content of the sources file is as follows:
Targetname = helloworld // driver name
Targetpath =. // The compiled sys path
Targettype = driver // The driver type is

Sources = helloworld. C // only one source file exists.

With these two files, you can use build to compile them. Go to the "Start" menu/Program/Development Kits/Windows 2000 DDK,
There are three cmd programs: 1) Checked 64-bit Build Environment, 64-bit version of "debug"; 2) Checked Build Environment
"Debug" 32-bit version; 3) Free Build Environment, "release" 32-bit version. Needless to say, it must be free build environment.

New or updated msvc detected. Updating DDK environment ....

Setting environment for using Microsoft Visual C ++ tools.
Starting dirs creation... completed.

C:/ntddk> Cd/

C:/> Cd helloworld

C:/helloworld> build
Build: object root set to :=> objfre
Build:/I switch ignored
Build: Compile and link for i386
Build: loading c:/ntddk/build. dat...
Build: computing include file dependencies:
Build: Examining C:/helloworld directory for files to compile.
C:/helloworld-1 source files (127 lines)
Build: Saving C:/ntddk/build. dat...
Build: compiling C:/helloworld directory
Compiling-helloworld. C for i386
Build: Linking C:/helloworld directory
Linking executable-i386/helloworld. sys for i386
Build: Done

1 file compiled
1 executable built

C:/helloworld>

Now there is helloworld. sys in the C:/helloworld/i386 directory.

========================================================== ========================================================== ================================
The only difference between Driver Installation and service installation is that when a service is created, the type of the driver is kernel driver. Other drivers are similar to operating services.

Driver Installation Process:
1. Call openscmanager () to open the Service Control Manager.
2. Call createservice () to create a service. The service type is kernel driver.
3. Call openservice () to obtain the Service handle.
Start the service
4. Call startservice () to start the service.
Stop Service
4. Call controlservice () to stop the service.
Delete a service
4. Call deleteservice () to delete the service.
5. Call closeservicehandle () to close the Service handle.

Operation driver process:
1. Call createfile () to obtain the device handle.
2. Call deviceiocontrol () to pass the I/O control code
3. Call closehandle () to close the device handle.

Http://www.xfocus.net/tools/200411/882.html
Here is a complete driver installation program, so I will not write it, just give the code to operate the driver

Complete code:

# Define debugmsg

# Include <windows. h>
# Include <winioctl. h>
# Include <stdio. h>

# Define device_hello_index 0x860

# Define start_hellpworld ctl_code (file_device_unknown, device_hello_index, method_buffered, file_any_access)
# Define stop_hellpworld ctl_code (file_device_unknown, device_hello_index + 1, method_buffered, file_any_access)

# Define erron getlasterror ()

# Define my_device_name "//. // helloworld"

# Define my_device_start "-start"
# Define my_device_stop "-Stop"

Bool drivercontrol (tchar * maik );

Void usage (tchar * paramerter );

Int main (INT argc, tchar * argv [])
{
If (argc! = 2)
{
Usage (argv [0]);
Return 0;
}

If (strcmpi (argv [1], my_device_start) = 0 | strcmpi (argv [1], my_device_stop) = 0)
Drivercontrol (argv [1]);
Else
{
Usage (argv [0]);
Return 0;
}

Return 0;
}

Bool drivercontrol (tchar * maik)
{
Handle hdevice = NULL; // device handle
DWORD retbytes = 0;

// Obtain the device handle
Hdevice = createfile (my_device_name, generic_read | generic_write, 0, null, open_existing, file_attribute_normal, null );

If (hdevice = invalid_handle_value)
{
# Ifdef debugmsg
Printf ("createfile () getlasterror reports % d/N", erron );
# Endif
Return false;
}

// Start
If (strcmpi (maik, my_device_start) = 0)
{
// Pass the started I/O control code
If (! (Deviceiocontrol (hdevice, start_hellpworld, null, 0, null, 0, & retbytes, null )))
{
# Ifdef debugmsg
Printf ("deviceiocontrol () getlasterror reports % d/N", erron );
# Endif
Closehandle (hdevice );
Return false;
}
}

// Stop
If (strcmpi (maik, my_device_stop) = 0)
{
// Pass the stopped I/O control code
If (! (Deviceiocontrol (hdevice, stop_hellpworld, null, 0, null, 0, & retbytes, null )))
{
# Ifdef debugmsg
Printf ("deviceiocontrol () getlasterror reports % d/N", erron );
# Endif
Closehandle (hdevice );
Return false;
}
}

If (hdevice)
Closehandle (hdevice); // close the handle

Return true;
}

Void usage (tchar * paramerter)
{
Fprintf (stderr, "============================================== ===================================================================/N"
"Driver version Hello World/N"
"Author: dahubaobao [est]/n"
"Homepage: [url] www.eviloctal.com [/url] or [url] www.ringz.org [/url]/n"
"Email: [email] dahubaobao@eviloctal.com [/Email]/n"
& Quot; OICQ: 382690/n & quot"
"% S-Start/t start/N"
"% S-stop/t stop/n"
"This program is only used for code communication. Please include any errors! /N"
"============================================== ===================================================================/N"
, Paramerter, paramerter );
}

Header file helloworld. H code:

# Ifndef _ helloworld_h __
# DEFINE _ helloworld_h __

# Include <ntddk. h>

# Define device_hello_index 0x860

// Two IOCTL macros
# Define start_hellpworld ctl_code (file_device_unknown, device_hello_index, method_buffered, file_any_access)
# Define stop_hellpworld ctl_code (file_device_unknown, device_hello_index + 1, method_buffered, file_any_access)

# Define nt_device_name l "// device // helloworld" // device name
# Define dos_device_name l "// dosdevices // helloworld" // symbolic connection

Ntstatus helloworlddispatch (in pdevice_object deviceobject, in pirp );

Void helloworldunload (in pdriver_object driverobject );

# Endif

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.