January 1998
Download Hood.exe (23KB)
Matt Pietrek is the author of Windows 95 System Programming Secrets (IDG Books, 1995). He works at NuMega Technologies Inc., and can be reached at mpietrek@tiac.com or at http://www.tiac.com/users/mpietrek. |
A short time ago, a coworker mentioned a hidden feature in one of our company's programs, invoked by pressing a button with an associated hotkey. This feature is hidden because the button doesn't show up in the dialog. Rather, the coordinates of this button lie outside of the dialog's bounding rectangle. Because dialog buttons are child windows of the dialog, and since child windows aren't visible if they're outside of their parent, the button just isn't displayed.
I'm not giving away any state secrets here. Placing controls outside of their parent dialog is a common trick. A related trick is to not set the WS_VISBLE flag for the control. In either case, the control isn't visibleat least not until the program takes explicit steps to move the control to within the dialog boundaries, or sets the control to the visible state. While thinking about this dialog trickery, I realized that I'm not aware of any programs that explicitly inform you of these tricks. With this thought, a column was born. In addition to finding undocumented dialog controls, I realized that the same program could also be used to check if you hadn't somehow moved a control outside of its dialog and forgotten to clean it up when you saved the dialog resource. This really happens! You're probably aware that dialogs are stored as resources within executables. If an executable is loaded by the Win32® loader as part of a process, you can use APIs like EnumResourceTypes, EnumResourceNames, and LoadResource to access resources. But there's no Win32 API support for accessing resources in a file that hasn't been loaded by the Win32 loader. Ever since I wrote my PEDUMP article (MSJ, March 1994), many people have asked how to access the data for an arbitrary resource. Finding hidden dialog controls was just the excuse I needed to break down and write some C++ classes that traverse the perilous PE resource format. Even after making it trivial to obtain a pointer to any resource in a file, dealing with the intricacies of the dialog box format was a real joynot! That part of the story can wait till later. For now, let's start by looking at the PE resource classes I wrote. You'll probably find them handy if you work with dialogs, icons, string tables, or other resource types. All of the classes are prefixed with PERsrc. The PERsrcTbl class represents all of the resources within an executable. Using methods of the PERsrcTbl class, you can obtain a pointer to a PERsrcType class instance. A PERsrcType represents all of the instances of a given resource type within an executable (for example, all of the dialogs). Methods of the PERsrcType class in turn give you instances of the PERsrcInst class. A PERsrcInst represents exactly one resource (for example, a particular dialog). The relationship of these three classes mirrors the Win32 resource format. Expressed as a hierarchy, the classes look like Figure 1. In the sources to the PERsrc classes, you'll see a number of member variables and methods. If you really want to
To make the PERsrc classes easier to use, I declared all of the internal methods and member variables as private or protected. Only the methods that you'd use in your code are public. When you filter out everything but the public methods, the class methods are pretty straightforward, as you can see from Figure 2. The first step in using the PERsrc classes is to declare a class instance of type PERsrcTbl. The constructor takes the name of an executable file and, under the hood, goes through all the gyrations to map the executable into memory. The destructor undoes everything, so no explicit cleanup is needed for this class. Once a PERsrcTbl is ready to roll, the next step is to get pointers to PERsrcType instances. PERscTbl::GetNextResourceType lets you iterate through all the resource types in the file (dialogs, bitmaps, and so on). Pass a null pointer for the first call to GetNextResourceType to start the enumeration. In subsequent calls, pass the PERsrcType pointer obtained from the prior call. Internally, this method deletes the PERsrcType passed to it, so you don't have to explicitly delete the PERsrcTypes you get back. The two other ways that a PERsrcTbl returns a PERsrcType is via the GetResourceTypeByName and GetResourceTypeById methods. Use GetResourceTypeByName when you have a named resource (such as AVI or TYPELIB). More commonly, you'll use GetResourceTypeById, which expects the RT_XXX resource IDs defined in WINUSER.H (for example, RT_DIALOG). The PERsrcType instances that you get back from these methods should be explicitly deleted. After getting ahold of a PERsrcType, there's one more step before you and the resource data can achieve data bliss. From the PERsrcType, there are three ways to get a PERsrcInst. A PERsrcInst corresponds to a particular resource of a particular type. The PERsrcType::GetNextResourceInst method enumerates through all the instances of a resource type. Passing 0 to the method begins the enumeration. To continue, pass it the return value from the prior call. If you enumerate through all the resources, there's no need to explicitly delete any instance pointers from the enumeration. The GetResourceInstByName and GetResourceInstById methods look up a particular resource by its name or value. The return value in either case is a pointer to a PERsrcInst. When you're finished with the PERsrcInst, remember to delete it. The PERsrcInst class is where the resource rubber meets the road. The most important method is GetData. It returns a PVOID pointer that points to the honest-to-goodness resource data. As you'll see later, this is how my hidden dialog control program gets to the raw data for a dialog box. If you need to know how big the resource instance is, the class has the Size method. Other PERsrcInst methods to be aware of are Id, IsNamed, and Name, which tells you the name of this resource instance (for example, MyBadlyDesignedDialog). If IsNamed returns TRUE, it's a named resource and you should use the Name method to get its name. Otherwise, the resource is identified by its ID value (for instance, 100) and the Id method should be used. Let's look at a snippet of code to see how the PERsrc classes can quickly give you access to the data for a particular resource instance. For this example, you have an executable called BigAndSlow.DLL that contains an icon called INCOMPREHENSIBLEIMAGE. To obtain a pointer to this bitmap, use the following sequence: |
|
The PERsrc class code isn't trivial in implementation. I'll skip a blow-by-blow description of them since the code is sprinkled with comments. I also don't think that every reader wants to know all the gory details. However, it was necessary to describe their use before moving on to the task of identifying mysteriously absent dialog controls.
With the initial hurdles of just getting to the resource data for a dialog box behind me, let's now see how secret or forgotten controls can be ferreted out. This ends up being a nontrivial task. Like the maze of data structures and offsets that represent the resource hierarchy, reading the data out of a dialog box description is a challenge, particularly because of all the oddball situations. The primary reason it's difficult is that many of the data fields of a dialog box and its child controls may not be present. Or a particular field may be defined, but is so rarely used that it's hard to find an example to test with.
Into the Depths of Dialogs
|
|
Reading the optional fields that follow the DLGTEMPLATE is somewhat tricky, since the data may be represented by variable-length strings. Alternately, the field might instead be a WORD-sized ID value. If you want just one particular field, you still have to navigate through all the prior fields to find it. At the end of the dialog resource is an "array" of DLGITEMTEMPLATEs. Each DLGITEMTEMPLATE represents one control from the dialog. It's not really an array because each DLGITEMTEMPLATE is also of variable length. Thus, to get the details of a given control, you have to trudge through all the previously defined controls just to find where the control of interest begins. To make things even more fun, each new DLGITEMTEMPLATE must start on a DWORD boundary, so you can't just assume that the end of one control's data coincides with the start of the next control. This is what the ALIGN_DWORD macro in the code is there for. The DumpDialogItem function in DlgDumpHelper.CPP walks through all the fields of a DLGITEMTEMPLATE that's passed to it. Like the dialog structure itself, each control definition starts with a fixed structure (the DLGITEMTEMPLATE) and is followed by several variable-length fields. Their memory layout is as follows: |
|
The window class field can be either a registered window class name (see the RegisterClass API) or a number from 0x80 to 0x85. These numbers represent the standard controls that are typically found in dialogs such as buttons and listboxes. The GetDlgItemClassIdAsString function shows the precise mappings. The title of the control is really just the window text (see SetWindowText). For example, the title for a button will be its caption. The last field, the creation data, is for controls that need a blob of data to be initialized (for example, a custom control). A pointer to the creation data is passed to the control's window when it gets the WM_CREATE message. Between the DumpDialog and DumpDialogItem functions, you can see that it's relatively involved to find all the fields of the dialog and its controls. Since it's necessary to do all this work anyhow, I made the DlgDump program emit the fields as it encounters them (hence the name DlgDump). The DLGTEMPLATE fields are displayed with no indentation, and the DLGITEMTEMPLATE fields are all indented by two spaces. I also used crude bar separators (== and - -) to make it easier to tell where one component leaves off and the next one starts. After all the work of creating classes to read resource data and writing all the code to rummage through a dialog, it's finally possible to identify controls that are hiding in some way. Finding controls that don't have the WS_VISIBLE flag set is easy. This flag would be ORed in with the other window flags as part of the style field in the DLGITEMTEMPLATE structure. The first act of the DumpDialogItem code is to check for the absence of the WS_VISIBLE flag. If it's not set, the function emits this line: |
|
Finding controls that are outside of their parent rectangle is only slightly more work. One of the first things the DialogDump function does is make a RECT structure representing the dialog's window. Important point: the dialog resource in the DLGTEMPLATE is given in coordinates relative to the screen. In contrast, the DLGITEMTEMPLATE coordinates are relative to the dialog itself. To make it easy to compare the two coordinate systems, the RECT for the dialog is created relative to the screen point 0,0, which makes the two systems equivalent. As each control is encountered, DumpDialog creates a POINT structure from the control's coordinates. This POINT, along with the dialog's RECT structure, is passed to the PtInRect API. If PtInRect returns FALSE, the following is spit out: |
|
There's a slight hole in this algorithm in that it only checks that the top-left corner of the control is within its parent dialog. It would be pretty rare that a control would only partially fall within the dialog, and it would be visible in any event. Lest you think that I wrote all this code to find just one or two hidden controls, try running DlgDump on various EXEs and DLLs in the Windows system directory. Some of the hidden controls can be explained by dialogs that expand to show more controls when a particular button is pressed. Others are just strange, however. For example, run DlgDump on DRWTSN32.EXE from Windows NT 4.0. Within the Directory Browse dialog, there are two static labels, an edit control, a listbox, a combobox, and two buttons. If you fire up a resource editor (such as MSDEV.EXE) on DRWTSN32.EXE, lo and behold, there are a whole slew of controls bunched up off the right side of that dialog. It's almost as if the dialog creator put them on the dialog, moved them off for some reason, and forgot about them. Although it was a lot of work and code just to uncover hidden dialog items, I had an ulterior motive in doing so. My goal was to answer quite a lot of the email that I get about PE resources. As a by-product, you now have reusable C++ classes for accessing resource data in any Win32 executable. Finally, I've shown the basic structure of a dialog template and provided relatively straightforward code for navigating through one. It was a lot of work, but in the words of Steve Jobs, "The journey is the reward."
Have a question about programming in Windows? Send it to Matt at mpietrek@tiac.com From the January 1998 issue of Microsoft Systems Journal
|