Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml

AspAutocadCDot netExcelFox proHtmlJava
LinuxMathcadPhotoshopPhpSqlVisual studioWindowsXml

Examining Windows 95 Internals

windows



+ Font mai mare | - Font mai mic



Examining Windows 95 Internals

You are in a maze of twisty little VxD calls, all different. This is the predominant experience of anyone who has tried to analyze the internal behavior of VMM, IFSMgr, or any of the other 30 VxDs that together provide the close to 1000 services that comprise the heart of Windows 95. While nobody enjoys having to thread through Microsoft's code, it is a fact of life during VxD development that one eventually encounters problems where one's own bugs and/or poor Microsoft DDK documentation can conspire to force one to look "under the hood" and see what is really going on. Furthermore, since VxDs are essentially extensions of the Windows 95 operating system, an understanding of the overall structure of Windows 95 helps designers evaluate the impact their VxDs might have upon it. This article describes a tool, VxD Monitor (VxDMon), that makes it much easier to see the "big picture" of how Windows 95 operates at the VxD level.



VxDMon allows users to observe and analyze the internal behavior of Windows 95. The program enables one to investigate Windows 95's internal operation by tracing the execution of system calls through the operating system, and graphically depicting how the various services call one another. In addition, profiling information is collected indicating the amount of time used by each service, and all services it calls. The uses of this information are many: Programmers wishing to learn how a specific service is meant to be used can immediately see which other services call it, indicating its functionality within the operating system; those interested in performance can see the average number of cycles consumed by a service. The list of services called by a particular service provides a glimpse of the possible side effects of invoking the service, for example whether it can potentially block. Perhaps most importantly, the tool allows users of Windows 95 to easily see just how their cycles are being spent within the operating system, and to better understand how the many functions described in the DDK fit together.

Information provided by VxDMon

At its innermost, Windows 95 consists of a set of virtual device drivers (VxDs) that implement the core features of the operating system, including memory management, paging, disk I/O, mouse and keyboard support, DOS-box support, and a host of others. Each VxD exports a set of services, functions callable by other VxDs, that form the API for the subsystem it implements. For example, the Virtual Keyboard Device (VKD), exports services for defining hot-keys and filtering keyboard input. User applications access these services through a second API each VxD exports specifically for application use, and which is typically encapsulated within one of the Microsoft-provided DLLs. VxD developers, however, interact with these services directly, allowing them to perform virtually any operation available to the original writers of the OS.

VxDMon makes use of the fact that Windows 95 provides a mechanism for hooking a VxD service, such that the hooker gets control any time that service is invoked (see Examining VxD Service Hooking, April[?] '95). We use this feature to allow the user to hook and monitor virtually every service provided by Windows 95, or any subset desired. While Windows 95 executes, VxDMon accumulates information about any services being called. This information includes: the number of times the service is called, the services calling the function, the services called by the function, and, on Pentium-based systems, the time required for the service call to complete. Figure 1 illustrates the main window of VxDMon showing information for the most frequently called VxD services, during a sample period lasting about one-half second. Clicking on the title of a column sorts the information by that field.  The Ordinal of a service is the 32-bit number Windows uses to uniquely identify each service: the high 16 bits indicate the VxD, and the low 16 indicate the service within that VxD. Because some VxD services may not return promptly, for example those blocking on a semaphore or even terminating the caller, the display includes both entry and exit counts.

Figure 1. VxD services called during a half-second interval.

The cycles required by the service are computed using the RDTSC (read time-stamp counter) instruction provided on the Pentium, which returns the number of processor cycles since the last processor reset. Timing information is not available when VxDMon is run on pre-Pentium systems, but all the other features are useable. Because the instrumentation imposed by VxDMon causes services to take slightly longer to execute than normal, during initialization the program performs a self-calibration and automatically adjusts the displayed times to compensate for the overhead.  On our system we see an overhead of about 160 cycles per call, which is 1-2 microseconds on most systems.

Double-clicking on a service brings up a new window, shown in Figure 2, that depicts all the services called by that service, as well as their descendants in turn. The user can similarly summon up all ancestors (callers) of a service, as shown in Figure 3. Items in the caller/callee trees can then be selected to bring up their ancestors and descendants, quickly providing an overview of all the possible connections between the various services. Information from any of the views can be saved to a file for later reference.

Figure 2. The "descendants" window, showing all services called by a service.

Figure 3. The "ancestors" window, showing all services calling a service.

Selecting Services to Monitor

When monitoring services, the common case is for a user to simply hook all VxDs in the system, since it is difficult to predict which VxDs may be involved in any given activity. However, if desired the user of VxDMon can select any subset of VxDs or services for observation. Figure 4 shows the service selection window, in which green services are hooked, red are not, and yellow indicates that a fraction of the underlying services are hooked. Selecting an item automatically selects all sub-items, so the user can quickly choose to hook everything, nothing, or anything in between.

While VxDMon can hook virtually every service in the system, there are a few services that Windows specifically prevents from being hooked, without explanation. An undocumented table specifies a list of services within VMM that may not be hooked, though in our own investigation we found that modifying the table to permit hooking resulted in no ill effect to the system. (Windows 95's use of this table can be seen by using a debugger to trace through the Hook_Device_Service).

VxDMon retrieves the list of existing VxD services from a text file, allowing it to be easily updated as new VxDs are added to the system. This means that the user can include VxDs from his or her own projects, and have them included in the output along with the native Windows 95 services. With the resulting profiling information one can quickly see the performance overhead of new VxDs, as well as the interactions with the rest of Windows 95. If any VxD is not loaded, or if a service cannot be hooked, VxDMon displays a window indicating the problem, but continues to hook all remaining services.

VxDMon includes an extensive on-line help facility, providing comprehensive descriptions of the options and windows presented to the user.

Figure 4. Window for selecting VxD services to be hooked.

How it works

As mentioned earlier, VxDMon relies upon the service hooking features of Windows 95. Because VxDMon must be able to hook any service in Windows without specific knowledge of parameter passing conventions, and because it must gain control on both function entry and exit, a vanilla service-hooking implementation is insufficient. This section describes how VxDMon addresses these problems.

Services are hooked using the Hook_Device_Service service, provided by VMM. Hook_Device_Service is provided with the ordinal of the service to be hooked and the address of the hooking function, and it returns the address of the original service handler. Once hooked, all calls that would normally go to the original service handler instead go to the hooking function, which can then perform intermediate processing and then transfer control to the original service.

When invoked, hook functions are not provided any context except that given the service they replace. If a single hook function is used to hook multiple services, there is no way, upon invocation, for the hooker to determine which of the original services is being called. This means that a separate hook function must be used for every service that is hooked. Because the set of services supported by VxDMon is determined dynamically (they are read from a file), a large pool of generic hook functions is maintained that can be drawn upon whenever the user asks to hook a new service. The generic hook is then instantiated with service-specific information, which it propagates to a more general back-end function that records and maintains statistics during each invocation.

When the original service is invoked, the hooking function must ensure that all parameters, whether they be stack based (for C-style services) or register based (for assembly-style) be preserved. If the original handler's parameter-passing convention is unknown, then neither registers nor the stack can be modified before invoking the original service. This introduces a problem for VxDMon, because we would like to invoke the original service using a CALL instruction, which returns control to us after the service completes.  Unfortunately, CALL pushes a return address on the stack, which will disrupt the offsets at which the service can expect to find its parameters on the stack (in cases where they are stack-based). One solution is to make a copy of the parameters on the stack before making the call, but this requires knowing the number of parameters on the stack and is, besides, a time-consuming operation. The alternative is to use a JMP to transfer control to the original service, but this means that when the service returns it will transfer to the original caller, rather than to VxDMon's hook.

This catch-22 is solved by modifying the return address on the stack of the original caller to instead point to a function within VxDMon, at the same time saving the original address elsewhere. Then, when the service returns, control is transferred back to VxDMon, where postprocessing and a jump to the original return address is performed. Simple? Not quite. The problem is that many services can be outstanding simultaneously, potentially executing on behalf of separate threads on separate stacks, so it is not obvious when a service returns which service it is, and in the case of recursive calls, which invocation it is associated with. For example, service A is invoked, which then invokes service B, which blocks on a semaphore. A context switch then occurs in which the new thread also invokes service B and blocks. The original thread's semaphore is then signaled, and service B returns, but not for the most recent call. Later, the second call to service B returns, and then finally the call to service A. Obviously, due to the possible scheduling decisions in Windows 95, things are more complex than they appear at first glance.

VxDMon solves this problem by associating a unique object with every outstanding service request. When a service is invoked, a new object is allocated from a free list and initialized with the original return address of the service's caller, the address of that return address on the stack, a pointer to the data structure associated with the service being hooked (containing the service-specific statistics) , and most importantly, a small piece of code that can be used as the return address for when the service completes. Now, when the service returns control passes directly to the object that knows all about its invocation. That object updates the relevant statistics and then jumps to the original caller's return address, completing the invocation.

There are a few more issues the implementation had to address. First, a mechanism is needed for detecting which services call which. Given that we can detect when a service is called and returns, a simple stack of the current outstanding services suffices to indicate the lineage of the service currently being invoked. Again, however, we have the problem that Windows can perform a context switch at any time, switching to a new thread that may subsequently call services completely unrelated to the service currently on the top of our stack of outstanding services. The problem is resolved by realizing that any context switch necessarily requires changing the ESP register to a drastically new value, since each thread uses a different call stack.  By saving the ESP value associated with the service most recently invoked, we can compare it with the ESP for a newly invoked service and ensure the difference is reasonable for a single VxD call. If not, the new service is not identified as a descendant of the previous service.

The last issue to address is how to safely unhook any hooked services when the program exits. Again, forces conspire against a simple solution. The principle issue is that some service calls may be outstanding when the VxD unloads itself, such that when the service returns to the monitoring code, the code is no longer there. Recall that in the object associated with each service invocation we saved the original return address as well as the address on the stack where the return address was originally located. Before exiting, we use this information to patch all outstanding return addresses with their original values, replacing the return values that point into VxDMon. This approach almost works, except that in some cases the threads that invoked the service have already terminated, and they no longer have a stack or return address to patch! In order to avoid patching an invalid address, or in the worst case, an address that has been reallocated to some other, unrelated process, we first use the _PageCheckLinRange VMM service to ensure that the address exists, and if so, that the return address being patched really does point back into VxDMon. If these conditions are met it is safe to modify the return address to its original value.

Conclusions

VxDMon is a tool for investigating the performance and behavior of VxDs executing under Windows 95. It allows users to see the flow of control through the many services that provide the core operating system functionality, as well as the performance of those services. Finally, VxDMon is easily configured to also profile developer's VxDs and demonstrate their interactions with the rest of Windows 95.



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 1266
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved