Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Examining VxD Service Hooking


MAY96: Examining VxD Service Hooking

Examining VxD Service Hooking

Monitoring, altering, or otherwise changing parts of Windows

Mark Russinovich and Bryce Cogswell

Mark is a developer at NuMega Technologies, and Bryce is a researcher in the computer-science department at the University of Oregon. Mark can be reached at [email protected], and Bryce can be reached at [email protected].


One reason DOS ruled the desktop is that programmers could easily augment and even change its behavior. A popular way of doing this was to write a terminate and stay resident (TSR) program that would intercept application requests for various DOS services and modify the requests before passing them along to DOS, or even service the requests directly. Thus, even without access to DOS source code, anybody with a C compiler or assembler could create their own flavor of DOS.

Windows 3.1 and Windows 95 also make their internal services visible to any program written to take advantage of them. The heart of Windows consists of a few dozen virtual devices (VxDs) that together create a substrate upon which the more-familiar USER.EXE, KERNEL.EXE, and GDI.EXE run. Sometimes VxDs manage the underlying hardware privately, but in most cases, VxDs export services that other VxDs, including the Windows kernel VxD, VMM, can call. Windows is designed so that a client requesting a service provided by a VxD is dynamically connected with the service provider. In other words, a client VxD can request a service from another VxD without knowing the location of that service location in memory.

Dynamic linking is achieved by calling services indirectly, through a jump table associated with each VxD, analogous to the interrupt vector table (IVT) used for DOS services. This dynamic-linking functionality allows VxDs to hook, or intercept, the services of other VxDs, much the same as one interrupt service routine (ISR) could be substituted for another under DOS. The hooking VxD can examine the request's parameters, modify them, and even replace the original service's functionality with a new one. This flexibility makes Windows, like DOS in the old days, fully extensible and replaceable.

In this article, we'll describe the different types of service hooking and their implementation under Windows 3.1 and Windows 95. We'll then present VCMon, an application that uses service hooking to monitor some behavioral aspects of the Windows 95 disk cache (VCACHE), such as cache hit and miss rates, which cannot be seen by any other means.

Why Hook?

Service hooking falls into three categories: monitoring, altering, and replacing.

  • Service monitoring. This is a passive form of hooking where the hooking routine simply examines the input and output parameters before passing them to the original service. For example, a serial-port monitoring application can hook the Windows serial port VxD's (VCOMM) services and record all data sent and received over a serial line by Windows programs. Monitoring hooks also allow one to determine when certain system states have been entered, such as a DOS box becoming a full-screen DOS session or memory resources falling below a certain threshold.
  • Service altering. This is less common than monitoring because the clients' assumptions about the service being performed must be carefully preserved. If the parameters are altered so that the assumptions are violated, the system may become unstable. Parameter altering is usually safe and useful in remapping the keyboard: The VxD service that receives keyboard input is hooked, and the hook routine modifies the appropriate scan codes before passing them on to Windows.
  • Service replacing. This, too, is rare because the hooking VxD usually must take over the entire functionality of the VxD whose services it is hooking. Assumption preservation applies here as well: The hooker must fully understand the entire specification of the services being replaced. Service hooking has recently been used in some memory- compression software, including some best-selling Windows programs. These programs replace the services of the Windows pagefile VxD with their own versions, which cache compressed paging data in RAM for faster access.

How Service Hooking Works

All VxDs contain a Device Declaration Block (DDB), a data structure that contains all the information required by Windows to integrate a VxD into the Windows kernel; see Figure 1. This includes the device's name, unique numeric identifier (assigned upon request by Microsoft), and initialization order number, as well as the address of the device's control entry point and a pointer to a list of additional entry points (known as the "service table") to all the services the VxD exports. Windows uses the device identifier and the service table to dynamically look up and call VxD services.

When programming in assembly, the programmer codes VxD requests to other VxD services as an assembly-language macro (VxDCall or VMMCall). This expands to a software interrupt 20h followed by a 4-byte constant in the VxD's assembly code. The constant contains the device identifier of the VxD that provides the requested service in the high word, and the service number, as determined by the service's index in the service table, in the low word. For instance, a call to the Windows PageFree service looks to you like Example 1(a), but is translated into the code in Example 2(a). If programming in C, the programmer codes a library call as shown in Example 1(b), which results in the invocation of the previous code.

When the Windows kernel executes an interrupt 20h, it looks at the constant following it and searches its table of registered VxDs for one with a matching identifier. If it finds one, it indexes into the VxD's service table to find the address of the requested service. For the sake of efficiency in future executions of the code that generated the trap, it replaces the int 20h/constant combination with an indirect call instruction that jumps through the target VxD's service table. Both forms require exactly six bytes of code, so the replacement is a perfect fit; see Example 2.

The level of indirection through the service table makes service hooking possible. Windows 3.1 introduced the basic form of service hooking using a VxD service in the Windows kernel called Hook_Device_Service. Example 3 presents the assembly language and equivalent C form (from the VtoolsD VxD C wrapper library) for a call to this service. The GetVxDServiceOrdinal statement is a macro that simply takes the name of a service, converts it into its VxD identifier/service number pair, and stores it into the specified register. The header files in the DDK define __service macros yielding ((device_id << 16) | service_number) for all services exported by Windows.

When a service is hooked, Windows takes the new hook procedure's address, places it in the service table, and returns the address originally contained in the service table to the calling code. From that point on, the hook procedure becomes the service handler (since all calls are done indirectly through the table). Therefore, it is at the hook procedure's discretion to call the routine whose address it received as a result of the hook action. By the time a VxD hooks a service, other VxDs may have already done so, and the returned address may be of another hook procedure rather than the original service. Hooking a service creates a hook chain that starts at the service table and can extend through hook routines all the way to the original service, as in Figure 2. Example 4 is a Windows 3.1 version of an example hook procedure.

Before Windows 95, once a VxD had hooked another VxD's services, it remained on the service chain forever. There was no way to unhook a service because Windows couldn't determine where the next hooker stored its "previous-on-the-chain" address in order to restore it (the analogy to unhooking an ISR under DOS should be apparent). To solve this problem, Windows 95 introduced a special hook-procedure definition that lets Windows determine the location of the procedure next on the chain following the VxD.

To take advantage of Windows 95's unhook capability, a hook procedure must be defined as type HookProc (if a C wrapper library designed for Windows 95 is being used, this is taken care of by the run-time library); see Example 5. When this code is assembled, a special header at the front of the routine is created for unhook support, as in Example 6. The first jump instruction and the opcode of the following jump in the generated code form a 4-byte constant (for example, a signature) since the jump is relative. The Hook_Device_Service routine checks for this constant. If it finds the constant, Hook_Device_Service treats the procedure as a Windows 95-friendly hook procedure. This allows it to store the address of the variable that is to receive the previous hooker's address as the target of the second jump instruction. These instructions are never executed; they only exist as part of the header structure.

When a service is unhooked, Windows traverses the service chain down to the procedure being removed by following the pointers in each hooker's header. Windows splices the procedure out of the chain by moving the procedure's Prev- InChain pointer into the PrevInChain pointer of the hooker at which it pointed or into the service-table entry. Unhooking is demonstrated in Figure 3, and unhooking example code is shown in Example 7. In Figure 3, the middle hook procedure is being removed from the service chain. The next hooker (to the left) in the chain has its previous pointer, pictured with scissors cutting it, modified to point at the same location as did the removed procedure. In this case, that location is the original service.

If any service on the chain between the service-table entry and the hook procedure being removed does not comply with the Windows 95 hook-procedure definition, the unhook will fail and the VxD must support the hook procedure until Windows exits.

VCMon: Service Hooking in Action

VCMon is an application that demonstrates interesting aspects of service hooking. (The entire VCMon system is available electronically; see "Availability," page 3.) It is made up of a Win32 GUI and a VxD that together monitor and display information about the behavior of VCACHE, the Windows 95 disk cache VxD. System Monitor (under "Start/ Programs/Accessories/System Tools" if you installed it during Windows 95 setup) can show the current size of VCACHE, but nothing else, whereas VCMon is able to monitor much more by utilizing service hooking; see Figure 4.

VCMon hooks several VCACHE services, including one that does a cache lookup to see if a block associated with a designated disk buffer is present (a cache hit) or not (a cache miss). The hook procedure for the routine converts certain types of requests into two separate requests with modified parameters. This allows VCMon to distinguish cache hits from misses. Converting one request into two is an example of modifying a service to do more than was originally intended. When VCMon determines the result of a request, it updates a statistics data structure that is shared with the GUI.

Another service that is hooked is the service that Windows calls to see if VCACHE wants to report any misses or hits. This information, in conjunction with the current amount of free memory and page fault rate, is used by Windows to determine if VCACHE should be told to shrink or grow. For informational purposes, VCMon also hooks the pagefile VxD's read/write routine, which is invoked whenever there is a page fault.

The VCMon GUI communicates with the VxD via DeviceIOControl calls where, at a rate that can be specified through the Rate button on the GUI, the VxD wakes up a thread in the GUI, telling it that new statistics are available in the shared-statistics data structure. The GUI displays the statistics as accumulated numbers in the dialog and, if a check box associated with an entry is toggled, as a graph. The Reset button clears all the accumulated statistics and sets all open graphs to start drawing at their left-hand sides.

While the VCMon VxD is very short, it provides information about Windows' inner workings that is not otherwise available. With little additional effort, VCMon can be extended to hook and display statistics about any VxD service.

Conclusion

VxD service hooking is not useful for everyone, just as DOS TSRs were not, but the ability to monitor, alter, or change parts of Windows means that enhancements are limited only by imagination and programming ability.

For more information: The Windows 95 DDK. See VMM.HLP\ Virtual Machine Manager\Miscellaneous Services for documentation on service hooking and STDVXD.HLP\Virtual File Cache Services for information on VCACHE.

Figure 1: The DDB and service table.

Figure 2: A hook chain.

Figure 3: Unhooking.

Figure 4: Running the VCMon program.

Example 1: Programmer's view of a VxD call. (a) Assembly version; (b) C version.

(a)
VMMcall _PageFree, <hMem, flags>

(b)
PageFree(ULONG hMem, ULONG flags);

Example 2: Generated code for a VxD call. (a) Before first execution; (b) after first execution.

(a)
int 20h  ; VxD service trap
dd 1055h ; hi word == VMM.VXD id, lo word == service #

(b)
call    [VMMServiceTable+55h*4]     ; indirect through table

Example 3: Hooking a service. (a) C version; (b) assembly version.

(a)
PrevInChain = Hook_Device_Service(
   GetVxDServiceOrdinal(Service),
   HookProc, &thunkHookProc);

(b)
GetVxDServiceOrdinal eax, Service
mov     esi, OFFSET32 HookProc     ; points to the hook
          ; procedure to install
VMMcall Hook_Device_Service    
mov PrevInChain, esi

Example 4: Windows 3.1 hook procedure.

PrevInChain     dd  ?        ; address of previous service
BeginProc HookProcedure
     call    [PrevInChain]   ; chain to previous service
     ret
  EndProc HookProcedure

Example 5: Windows 95 hook procedure.

PrevInChain     dd  ?
BeginProc HookProcedure, Hook_Proc PrevInChain
        Call    PrevInChain
        ret
EndProc HookProcedure

Example 6: Windows 95 generated hook-procedure header.

    jmp HookProcedure   ; signature
    jmp [PrevInChain]   ; previous-in-chain ptr
HookProcedure:
    ...

Example 7: Unhooking a service. (a) C version; (b) assembly version.

(a)
Unhook_Device_Service(
    GetVxDServiceOrdinal(Service),
    HookProc,
    &thunkHookProc );

(b)
GetVxDServiceOrdinal eax, Service
mov        esi, offset32 HookProc
VMMCall    Unhook_Device_Service

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.