|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Contents
IntroductionThe article presented here is targeted at all programmers wanting to develop add-ons or plug-ins for the Windows Live Messenger. Although the sample code is written in C++, the theoretical and practical concepts involved are implementable on most programming languages. On the other hand, the article should be useful for conceptually grasping the methods and techniques that can be practical in the fields of application functional and interoperability research, Operating System internals, and/or general reverse engineering duties, considering that most of the exposed "tricks" are applicable to a wide range of Windows applications. Necessary skills and toolsThe minimum required skills to follow the article are a basic understanding of the Windows API, C/C++, COM, and x86 architecture. It is also recommended to be able to use additional development tools in a fluent manner, such as API and message spying software, executable examination utilities, etc. The following tools and libraries are highly recommended, and some of them are used throughout the article:
Article layoutThe article follows a progressive approximation to the topic of plug-in development. It's organized in the following sections:
Explains the research and development of a proxy DLL to inject code into the target application address space using tools for exploring imports and exports of PE executables, along with guidelines for implementing the stubs for forwarding proxy DLL calls. This is the main section of the article, and approaches the following topics: How to build and use the sourceNote: The code is targeted at Live Messenger 8.5. Other versions may require modifications, specially on the resource hooking area. The source code implements all the techniques explored in the article, and it's licensed under the GNU General Public License. To successfully test the generated DLL on your Windows Live Messenger local installation, you need to:
As long as you conform to the GPL, feel free to modify the code and/or to use it as a base for your own ideas. How to quick-start with the precompiled DLLsYou may want to use the prebuilt libraries included with this article instead of building the solution in Visual Studio: just close any running msnmsgr.exe, and copy the three DLLs to the Windows Live Messenger directory. A word of cautionThe code included with this article was written with raw research in mind; therefore, many optimizations and solid coding patterns or practices may have been left out with the only intention to achieve results in a short time. Due to this, the code must be considered just as a practical implementation of the theoretical concepts exposed, or as a base for further research in the field of reverse engineering. With this advice in mind, let's begin with our first topic. Getting into the Live Messenger address spaceTo successfully modify WLM functionality without restrictions, it's essential to run our code within the WLM executable address space. There are various methods available to achieve this, such as Proxy DLLs, remote thread injection, PE patching, etc. In fact, you can find excellent articles in CodeProject about these topics. Fortunately, with the WLM executable, we can successfully inject our code using a simple technique known as Proxy DLL. The objective of this approach is to create a "fake" DLL which acts as a function call forwarder, effectively sitting in the middle of the target process and our to-be-injected code (typically another DLL). As long as the name of our fake DLL is in the import table of the target Portable Executable, our proxy DLL will get loaded, allowing us to run code in the target process' virtual address space. Of course, we can't simply load our inject-DLL, we also need to trick the target executable into believing that it is effectively executing the "original" DLL and not our fake version: we need to forward the calls to the original DLL. This is not hard to do as long as we can find an imported DLL in the target executable with a few exports. For the Windows Live Messenger case, a good choice, as we'll see, is MSIMG32.DLL, a small Windows system DLL that implements some GDI functions such as Windows Live Messenger importsWe want to implement the Proxy DLL approach to run code in the WLM address space. From the explanation above, it's clear that the first step is to examine the WLM executable PE imports. An excellent tool to inspect PE imports and exports is Dependency Walker. You can download the latest version from here. Opening Dependency Walker and choosing the Windows Live Messenger executable yields the following screen:
We begin examining each of the imported DLLs in the left-pane, looking at the number of exported functions of the selected DLL. You may ask why not choose the DLL based on the number of imported functions used by the WLM, instead of the total exported functions of the DLL. Our approach is more conservative as we'll forward all of the exported functions in our fake DLL; just remember, the general rule is to forward all the calls that are needed by the target executable. At the point we reach MSIMG32.DLL, we see that this is certainly an excellent candidate, just five exports:
We've chosen MSIMG32.DLL as the proxy DLL that will reside in the WLM executable directory, forcing it to load our fake DLL instead of the MSIMG32.DLL which is located in the Windows system directory. Note that we can force the loading of our DLL from the WLM executable directory since this DLL is referenced on PE imports therefore loaded by the NT Loader, not by the executable itself. Keep in mind that other applications may load DLLs manually with Details of the file system locations that are searched by the NT loader when loading DLLs can be found at MSDN: Dynamic-Link Library Search Order. Deep descriptions about the native Windows NT DLL loader, including DLL programming considerations, are located on various posts here. Implementation detailsOur implementation of the proxy DLL is found on the proxyDLL project inside the wlmplugindemo solution. The general execution flow of the code when the DLL is attached is:
Note that using Back to the implementation. Remember, we need to write "stub" function definitions with the only task of forwarding calls to the real DLL through the pointers obtained as explained above, thus we must respect the signature of the functions we want to forward. In the case of the five exports of MSIMG32.DLL, doing a search in the Windows API documentation yields the function declarations we're interested in: BOOL TransparentBlt (HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int hHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, UINT crTransparent); BOOL AlphaBlend (HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction); BOOL GradientFill (HDC hdc, PTRIVERTEX pVertex, ULONG dwNumVertex,
PVOID pMesh, ULONG dwNumMesh, ULONG dwMode);
These three functions are documented by Microsoft, and are easy to deal with. But, in the case of the parameters and return types of the remaining functions Searching on MSDN about At this point, we must tackle the problem of defining the return type and parameters of the remaining functions. Let's dump the exports of MSIMG32.DLL using the DUMPBIN utility. The output is: dumpbin C:\WINDOWS\SYSTEM32\MSIMG32.dll /Exports
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file C:\WINDOWS\SYSTEM32\MSIMG32.dll
File Type: DLL
Section contains the following exports for MSIMG32.dll
00000000 characteristics
8025BC7 time date stamp Sun Apr 13 16:15:19 2008
0.00 version
1 ordinal base
5 number of functions
5 number of names
ordinal hint RVA name
2 0 0000119B AlphaBlend = _AlphaBlend@44
3 1 0000110C DllInitialize = _DllInitialize@12
4 2 0000117F GradientFill = _GradientFill@24
5 3 000010F0 TransparentBlt = _TransparentBlt@44
1 4 000012C6 vSetDdrawflag = ?vSetDdrawflag@@YGXXZ (void __stdcall vSetDdrawflag(void))
Summary
1000 .data
1000 .reloc
1000 .rsrc
1000 .text
If you look at the symbols the linker generated for each function, you can infer the number of 32-bit parameters each function takes along with the calling convention used. Each name decoration format corresponds to a calling convention. According to the Visual C++ linker documentation at Name Decoration, all functions are using void vSetDdrawFlag(void); Now, UNKNOWN_RETURN_TYPE DllInitialize (DWORD p1, DWORD p2, DWORD p3); We're left with the return type -- we know this is a
Now, with this information on hand, we can write down the final declaration of the DWORD DllInitialize (DWORD p1, DWORD p2, DWORD p3); Before going on, let's go back a bit in our research to see another approach that can be taken, in general, when dealing with functions residing on DLLs. Let's check out the headers of MSIMG32.DLL by using DUMPBIN with the /HEADERS parameter. The following are the first lines dumped, where we can find the essential information: Microsoft Visual Studio 9.0\VC>dumpbin C:\WINDOWS\SYSTEM32\MSIMG32.dll /headers
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file C:\WINDOWS\SYSTEM32\MSIMG32.dll
PE signature found
File Type: DLL
FILE HEADER VALUES
...
...
OPTIONAL HEADER VALUES
10B magic # (PE32)
7.10 linker version
600 size of code
600 size of initialized data
200 size of uninitialized data
110C entry point (7633110C) _DllInitialize@12
1000 base of code
2000 base of data
76330000 image base (76330000 to 76334FFF)
1000 section alignment
...
(output follows)
BOOL WINAPI DllMain( __in HINSTANCE hinstDLL,
__in DWORD fdwReason, __in LPVOID lpvReserved);
This declaration matches our previous result where we established three parameters and a non-void return type, so our previous definition for BOOL WINAPI DllInitialize(HINSTANCE p1, DWORD p2, LPVOID p3); Since we won't use those parameters except for "feeding" our MSIMG32.DLL function pointers, we named the parameters as shown. This concludes our MSIMG32.DLL analysis. Now, we can finally state our function pointer typedefs and "stub" prototypes as follows: // // Function pointer typedefs for exports we are forwarding // typedef BOOL (WINAPI *PFNTRANSPARENTBLT) (HDC,int,int,int,int,HDC,int,int,int,int,UINT); typedef VOID (WINAPI *PFNVSETDDRAWFLAG) (VOID); typedef BOOL (WINAPI *PFNALPHABLEND) (HDC,int,int,int,int,HDC,int,int, int,int,BLENDFUNCTION); typedef BOOL (WINAPI *PFNGRADIENTFILL) (HDC,PTRIVERTEX,ULONG,PVOID,ULONG,ULONG); typedef BOOL (WINAPI *PFNDLLINITIALIZE) (HINSTANCE, DWORD, LPVOID); // Function pointer typedef to injected-DLL initialization typedef void (*PFNINITDLL) (void); #ifdef __cplusplus extern "C" { #endif // // Function protoypes // BOOL WINAPI TransparentBlt(HDC, int, int, int, int, HDC, int, int, int, int, UINT); BOOL WINAPI AlphaBlend (HDC, int , int, int, int, HDC, int , int, int, int, BLENDFUNCTION); BOOL WINAPI GradientFill (HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG); BOOL WINAPI DllInitialize (HINSTANCE,DWORD,LPVOID); VOID WINAPI vSetDdrawflag (VOID); #ifdef __cplusplus } #endif // __CPLUSPLUS The above code section is located on the msimgproxy.h header in the proxyDLL project. Note that we export all functions with the The only remaining file for a successful build of our proxy DLL is the DEF file, which is quite simple: LIBRARY "msimg32"
EXPORTS
vSetDdrawflag @1
AlphaBlend @2
DllInitialize PRIVATE
GradientFill @4
TransparentBlt @5
In this DEF file, we are respecting the ordinals of the real MSIMG32 library. The optional keyword Finally, the important chore of intercepting the calls that the Windows Live Messenger makes to our fake DLL, routing them to the original MSIMG32.DLL located in the Windows System directory, is done by simply using the function pointers to the real library we've previously obtained. The following code shows the forwarding stub for BOOL WINAPI AlphaBlend(HDC p1, int p2, int p3, int p4, int p5, HDC p6, int p7, int p8, int p9, int p10, BLENDFUNCTION dw) { return pfnAlphaBlend (p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,dw); } This finishes our dissertation about the MSIMG32 proxy DLL research and implementation. Windows Live Messenger hackingAfter the proxy DLL is built up and ready, we can begin developing some techniques for modifying the WLM functionality. We'll describe how to achieve the following:
Prior to this, let's see a brief but necessary description of Trappola, the API hooking library from Nektra. Introduction to hooking with TrappolaTrappola is an excellent free API hooking library from Nektra, which can be used to build robust API interception applications (such as the SpyStudio, also from Nektra). To explain Trappola, we'll see how it's practically used on this project, so you can follow the code along this article to better understand the concepts and usage of this excellent interception tool. Keep in mind that not all of the Trappola features are used on this project. To go deeper, feel free to explore and play with Trappola further yourself. Defining what, where, and how to hookIf you look at the hooking.h header in the wlmplugDLL project, you'll find
At this point, you should be able to understand each of the array entries of Hook attach and detachA hook becomes active when a In our project code, you can see how the hooks defined in Disconnecting a hook just involves removing the respective With this overview of Trappola in hand, we are ready to begin our first hacking of the Windows Live Messenger: menu additions through subclassing. You may think that menus in the WLM are standard Win32 menu resources, and you're right, but this is an exception, because most controls of the WLM are not standard, but owner-drawn. And, not just owner-drawn, but drawn with a custom UI called DirectUI, which is completely undocumented. The drawing surface for a DirectUI interface control is contained in its own window class, the child of the top-level window class where the menu resides. For this reason, an overview of the window-classes involved in the Windows Live Messenger application is detailed below. Windows Live Messenger window classesThe first surprising thing that any programmer interested in Windows Live Messenger reversing and/or hacking may encounter is that the client area of the Windows Live Messenger does not expose any child controls, but an unique "surface" where all controls seem to be painted on. Let's see this with Spy++; however, you can use any decent Win32 window spying utility, such as Winspector.
In the capture above, at the synchronized left pane, it's clear that the If you are interested, the DirectUI functionality is contained in msncore.dll in the Windows Live Messenger application directory. There, a lot of C++ exported functions related to this undocumented UI framework can be found using any PE export tool such as DependencyWalker or PE-Browse. Surely, a reversing task on this code could be very interesting, although of limited applicability since future versions of the Windows Live Messenger are likely to be based on WPF code, as a Web search about the latest 9.0 betas indicate. The top level window class is named Modifying the Windows Live Messenger menu barIn this section, our aim is to explain how to modify the WLM menu bar using API hooking. The Windows Live Messenger does not dynamically create the application menu bars, but they are loaded from stored resources on a separate DLL, msgslang.8.5.XXXX.XXXX.dll. You can confirm this using a resource editor such as Resource Hacker, as the following screenshot displays:
Note that your resource language identifier, known in the Win32 API as LANGID, may differ. In this capture, 3082 is the LANGID for Spanish language. For the menu modification to go, it's clear that the main window should have been created successfully. Thus, an excellent candidate function to intercept is About ANSI and Unicode APIsIn Windows 9X, all the Operating System API strings were ANSI, i.e., 8-bit character strings. The 256 character limitation was evident when non-English characters were used, e.g.: Asian languages. To provide solid and transparent support for application and system internationalization, Microsoft introduced Unicode character sets in the NT kernel series with 16-bits per character, also known as wide-strings. Unicode is the only standard string encoding in NT-based Operating Systems: all operations on strings in the Windows API are done in Unicode, so ANSI strings are converted to wide-character strings first. With the aim of compatibility with Win16 and Win9X applications, NT also introduced distinct versions of almost all API functions that took string parameters (exceptions exist such as #ifdef UNICODE #define CreateWindowEx CreateWindowExW #else #define CreateWindowEx CreateWindowExA #endif Then, if a program is compiled as Unicode, the call is the function name plus 'W' as suffix, or 'A' if the program is compiled to use ANSI strings. The fact is that Unicode software can run only on NT-based systems, ANSI software on all Win32 platforms. Nevertheless, if you want to develop applications to run under all Win32 platforms, you must use Our case is explicit and clear: the Windows Live Messenger code is 100% Unicode. With this fact, we'll go into hooking Intercepting and subclassingAs we've seen on the Trappola overview section, a L"user32.dll", "CreateWindowExW", NUM_PARAMS(12), Handle_CreateWindowExW, stdcall_, _call_after The void fn_Handler (NktHandlerParams* hp);
Don't worry now about Our approach to create the menu will be to intercept So, let's get our hands dirty with the hook handler code in wndhook.cpp: void Handle_CreateWindowExW (NktHandlerParams * hp) { LPCWSTR lpszWndClass = *(LPCWSTR*) PARAMETER_INDEX(1); if (lpszWndClass && HIWORD((INT_PTR )lpszWndClass) ) if (wcscmp(lpszWndClass, g_wlmTopWindow.szWndClass) == 0) { g_wlmTopWindow.hwnd = (HWND)hp->context.regs->EAX; g_wlmTopWindow.pfnOldWndProc = (WNDPROC) GetWindowLongPtr (g_wlmTopWindow.hwnd, GWLP_WNDPROC); SetWindowLongPtr (g_wlmTopWindow.hwnd, GWLP_WNDPROC, (LONG_PTR)WlmWndProc); } } There are many things to explain in the code section above, so let's do a line by line analysis.
In the handler shown, we need the second (index 1) parameter from You can see this is another use of the Therefore, when We must examine what window message to process to do this. case WM_SHOWWINDOW: hMainMenu = GetMenu(hwnd); hDemoMenu1 = CreatePopupMenu(); AppendMenu(hMainMenu, MF_STRING | MF_POPUP, (UINT_PTR) hDemoMenu1, L"W&lmPluginDLL"); AppendMenu(hDemoMenu1, MF_STRING, ID_MENU_DISPLAY_CONTACTINFO, L"Display Contact Information &Window"); AppendMenu(hDemoMenu1, MF_STRING, ID_MENU_DEMO_ABOUT, L"About..."); DrawMenuBar(hwnd); We get the menu handle from the top level menu using The remaining task is to catch case WM_COMMAND: // check if it's from our menu if (HIWORD(wParam) == 0) { switch (LOWORD(wParam)) { case ID_MENU_DEMO_ABOUT: ... ... break; case ID_MENU_DISPLAY_CONTACTINFO: ... ... break; } } Keep in mind that you must return the unprocessed messages to the original window procedure. This is the reason we saved the old window procedure address on the global return CallWindowProc (wndProc, hWnd, uMsg, wParam, lParam);
In the subclassed window procedure we've constructed, the old window procedure function pointer is placed by replacing the first parameter with Here is our result:
As you see, we've successfully added a working menu through hooking and subclassing. In the following section, we are going to explain how the "About" and "Contact Info" windows are implemented through subclassing. The "Colorized" window class: Practical subclassingIf anxiety won over you and you already tested the project successfully, you probably noted that the About window and the Contact Info windows are similar in terms of visuals and behavior, as the following capture shows:
Both windows are based on the same window class, the Colorized window class, defined as Window classes allow a form of inheritance of attributes and functionality to subclassed windows. Subclassed windows can override, accept, or ignore the window messages that are part of the default window class procedure. This is applicable to most UI elements classified as a "window" in the Win32 API, e.g.: edit boxes: a typical approach to validate an edit control's text while the user is typing is through subclassing the edit control. Nevertheless, we want to remark this very important point: window subclassing is not OOP inheritance and/or polymorphism. They are conceptually similar, but window subclassing is a Windows programming technique, not an implementation or application of a particular programming paradigm. With that potential confusion cleared out, let's continue. Opening the source file clrzwind.cpp and looking at its window procedure makes clear that windows based on this class will, by default, handle the following messages: When the window class is registered through Now, both windows ('About' and 'Contact Information') are created the same way. They define the window-specific properties they need, but specify void CreateAboutDlg(HWND* pHWnd) { *pHWnd = CreateWindowEx(NULL, wszColorizedWndClass, NULL, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CX_DIALOGSIZE, CY_DIALOGSIZE, NULL, NULL, hDllInst, NULL ); // subclass if (*pHWnd) { pfnColorizedWndProc = (WNDPROC) GetWindowLongPtr(*pHWnd, GWLP_WNDPROC); SetWindowLongPtr(*pHWnd, GWLP_WNDPROC, (LONG_PTR)AboutDlgProc); } } void CreateContactInfoWnd(HWND* pHWnd) { // Create the contact info window with the colorized window class *pHWnd = CreateWindowEx(WS_EX_NOACTIVATE, wszColorizedWndClass, NULL, WS_POPUP, 0, 0, CIW_DEFAULT_WIDTH, CIW_DEFAULT_HEIGHT, NULL, NULL, hDllInst, NULL ); // window subclassing if (*pHWnd) { pfnColorizedWndProc = (WNDPROC) GetWindowLongPtr(*pHWnd, GWLP_WNDPROC); SetWindowLongPtr(*pHWnd, GWLP_WNDPROC, (LONG_PTR)ContactInfoWndProc ); } } The technique to subclass both windows is the same we've seen before: we change the window procedure of each subclassed window to a specialized one where we can override the messages that are handled by the "parent" class window procedure, handle new ones, or even "reject" messages (this is the same as overriding, but we simply return from the window procedure without doing anything). The code structure of the subclassed window procedures involved should clarify this. Let's see the "About" window procedure first: LRESULT AboutDlgProc (HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
{
switch(uMsg)
{
case WM_CREATECHILDCTRLS:
if (!fChildCreated)
{
//
// Create child control (OK button) and load bitmap
//
break;
}
case WM_COMMAND:
if (LOWORD(wparam) == ID_OK)
// Handle OK button press
break;
case WM_PAINT:
{
//
// Paint logo and text
//
return 0L;
}
case WM_CLOSE:
//
// Inhibit WM_DESTROY!
//
return 0L;
}
return CallWindowProc(pfnColorizedWndProc, hwnd, uMsg, wparam, lparam);
}
Let's analyze this procedure. The So, according to our code, after we create the About window, we can call SendMessage (hwnd, WM_COMMAND, MAKELPARAM(ID_OK, 0), 0); As always, for the remaining messages, we pass them to the Colorized window class procedure using the Now, to finish our discussion about window subclassing, it's important to note that windows based on the Colorized class can be dragged from the client area using the case WM_NCHITTEST: // prevent dragging return 0L; With this general overview, you can see the details on the source code; the relevant files are clrzwind.cpp, aboutdlg.cpp, and cinfownd.cpp. Introduction to Windows Live Messenger resourcesIn this section, we are going to explore one of the most interesting sides regarding the internals of the Windows Live Messenger: the application resources. First, a general description of where and how the WLM resources are stored is presented, followed by a resource manager implementation that allows, e.g., to add buttons to the Windows Live Messenger toolbar. Where resources are storedThe resources used by the Windows Live Messenger can be classified as,
The first kind of resources are available at msgslang.8.x.xxxx.xxxx.dll, and are language-dependent as the DLL filename implies. The second kind of resources can be found on the msgsres dynamic link library. We said that those resources are stored in XML format, you can check that using Resource Hacker, for example; you'll find many resource types contained there, most identified by an integer. To begin our examination, an interesting resource type is #4004, where most Windows Live Messenger Direct-UI based dialogs are found. The following screenshot shows a Resource Hacker session displaying the XML description for the "Toast Window", which is the small pop-up box that emerges from your system tray when a message arrives, a contact gets connected, a new mail is at your inbox, etc.
Thanks to the research done at the MSN-modders forum mess.be, we can present the following list of resource types in the resource DLL with the corresponding interface element for each resource ID entry. The original link to the forum is here.
Additionally, in resource type #4005, the matching "styles" for each #4004 resource are found. We can think of this similarly as (X)HTML+CSS: the first defines the document content, while CSS, the visual properties -- with this analogy, XML resources with type #4004 describe the general content of each UI element, while the same resource ID located at the #4005 resource type in the tree specifies the visual and behavioural aspects of the matching element. An important thing to consider is that as far as we could find, XML elements are checked internally by the Windows Live Messenger as there isn't any XML schema for the resources to be validated in the DLLs we examined. Thanks to many enthusiast programmers reversing the internals of the DirectUI XML format, an XSL stylesheet is available where all Version 8.5 elements are defined. This incredibly useful file is found on the MemSkin+ Tools site. With all of this in consideration, we need to see what functions we need to hook to be able to intercept the WLM resources. Resource functions call sequenceThe four essential resource management functions of the Windows API, what parameters they need, and what type of value they return, are condensed on the following list:
Looking at the input parameters and output return values of the above functions, it's clear that for an application to be able to load a resource and use it, the sequence
Working with this overview in mind, our task is to properly hook the resource functions; remember, we said one of the objectives was to add buttons to the main WLM toolbar, this can be accomplished by hooking those DirectUI XML resources, but in which way? The first thing we need is the XML definition of the toolbar. Looking at the previous resource list, we can infer that the toolbar should reside on the Contact List XML resource: this resource is identified by ID #923 in the #4004 resource type. Inspecting the resource data with Resource Hacker yields a rather large chunk of text; nevertheless, searching for the text 'Toolbar' will lead us to the starting of the toolbar definition. This lies between the <MToolbar TextSuppressOption=Individual padding=rect(1,1,1,1) layoutpos=client>
<button active=mouse|nosyncfocus accessible=false
id=atom(ToolbarScrollLeft) class="HIGToolbarLeftScrollButton;"
tooltip=true/>
<button class="HIGToolbarButton" layout=borderlayout()
id=Atom(browsebarback) cmdid=40600 AccRole=43
Tooltip=true active=mouseandkeyboard|nosyncfocus Enabled=false>
<ButtonIcon class="HIGToolbarIcon" contentalign=middlecenter ID=Atom(ai13)/>
<element layoutpos=Right contentalign=middlecenter layout=filllayout()>
<ButtonText class="HIGToolbarText" contentalign=middlecenter Shortcut=1 ID=Atom(ai14)/>
</element>
</button>
...
...
...
<button class="HIGToolbarButton" layout=borderlayout()
id=Atom(browsebarhelp) cmdid=40608 AccRole=43
Tooltip=true active=mouseandkeyboard|nosyncfocus>
<ButtonIcon class="HIGToolbarIcon" contentalign=middlecenter ID=Atom(ai27)/>
<element layoutpos=Right contentalign=middlecenter layout=filllayout()>
<ButtonText class="HIGToolbarText" contentalign=middlecenter Shortcut=1 ID=Atom(ai28)/>
</element>
</button>
<button active=mouse|nosyncfocus accessible=false
id=atom(ToolbarScrollRight) class="HIGToolbarRightScrollButton"
tooltip=true/>
</MToolbar>
In each of the XML resource definitions, button[id=atom(ToolbarScrollLeft)]
{
padding:rcrect(20213);
FontSize:rcint(20074);
accdesc:rcstr(20105);
accname:rcstr(20106);
Content:rcstr(20104);
}
Properties for UI elements may be specified literally, or through the
Bitmaps are stored either on standard PNG format, or in a format where although the header bytes say "RLE", they are unreadable by most applications claiming to read Run-Length Encoding bitmaps. This is probably an internal file format used by Microsoft, since the only way to view them is to use the internal msncore.dll functions. A converter from RLE to PNG is available here. The resource management classReturning to the Windows API resource management functions topic and their interception by means of hooking, we are ready to present our approach for a general resource management class. The task of this resource manager is to track the resources to be hooked, allocate and deallocate virtual memory for additional resource data, and modify the resources we want at runtime. Our implementation is space-efficient since it allocates only the needed memory for additional data inserted on the existent resources, or for allocating new resources from the user; it also provides an execution upper-bound of O(log N) to minimize the performance impact on the target Windows Live Messenger process resource handling. The code for you to inspect is in resmgr.h and resmgr.cpp. Internal tablesThe resource manager class (from now on, RMC) maintains three tables, based on
A key concept is that our hooking work tries to respect the semantics of the original (unhooked) APIs, e.g., the The internal tables of the RMC and their general operations can be seen in the following schema (values are fictitious):
Built-in and external resourcesRMC supports either modifying any WLM resource that the application loads, or inserting new resources from an external DLL. For example, you can insert a toolbar button by telling the RMC to insert an XML definition into the Registering resourcesIn terms of the RMC, all resources to be acted upon (either coming from the WLM resource DLL or an external one) must be registered into the RMC tables. This functionality is provided by the two member functions of the resource management class, void CResourceManager::RegisterResource (DWORD dwType, DWORD dwName, LPVOID pvResData, DWORD cbResDataSize, UINT iResModType, UINT cbWhere = 0);
The above function is used for registering existing resources only. For using resources from the external resource DLL resdll.dll, the void CResourceManager::RegisterNewResource (DWORD dwType, DWORD dwName, const RESOURCEINFO& ri);
In the next section, we'll show how the RMC effectively manages the resources; for a complete class reference, please refer to the source code. Adding a toolbar button exampleIn this section of the article, a practical example of resource hooking and management will be shown. You can refer to toolbar.cpp and dllentry.cpp to follow the exposition of the technique. Open the dllentry.cpp source file, and scroll until the array definition that appears below: WLM_TOOLBARBUTTON tbb[2];
We are going to create two additional buttons; therefore, we define an array of two
Following the
The remaining member assignments are pretty self-explanatory. The last lines in the source file allocate a fixed buffer size for the XML UI resources and the XML style-sheets we talked about many times in the article. The Now, switch your current file to toolbar.cpp. The first lines of code are nothing uncommon: UINT uLastRcImg = RESOURCE_ID_BASE; UINT uImgId; DWORD dwBytePos = (ptbb->uPosition == TB_INSERT_FIRST ? TB_INSERT_FIRST_BYTE : ptbb->uPosition);
The main work is done inside the
When all the resource data related to the new toolbar buttons is finally defined in g_resMgr.RegisterResource(WLMRES_XMLSTYLE, WLMRES_CONTACT_LIST, szStyle, strlen(szStyle), RR_INSERT, WLMRES_STYLE_INSERT_POS); g_resMgr.RegisterResource(WLMRES_XMLUI, WLMRES_CONTACT_LIST, szXmlRes, strlen(szXmlRes), RR_INSERT, dwBytePos); For the current implementation of our project, both ins | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||