Click here to Skip to main content
Click here to Skip to main content

Windows Live Messenger Plug-in Development Bible

By , 3 Nov 2008
Rate this:
Please Sign up or sign in to vote.

Contents

Introduction

The 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 tools

The 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 layout

The article follows a progressive approximation to the topic of plug-in development. It's organized in the following sections:

  1. Getting into the Windows Live Messenger address space
  2. 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.

  3. Windows Live Messenger hacking
  4. This is the main section of the article, and approaches the following topics:

    • Intercepting Operating System calls using the Trappola library.
    • Discussion of the Windows Messenger window classes for which the first practical hooking is done by adding elements to the top-level menu bar.
    • A first view into the concept of window subclassing is offered, expanded later on with the creation of customized windows that match the Live Messenger current color scheme.
    • How the Windows Live Messenger stores and uses resources, including a description of how UI elements are defined with XML, both in visual and behavioural aspects. The analysis also explains the sequence of calls used by the Windows Live Messenger and most applications to find resources and load them through resource addresses.
    • The theory behind the resource management class (RMC) implemented on the attached VC++ project is explained along with the functionality. The practical use of this class is shown by adding a toolbar button to the Windows Live Messenger interface.
    • Microsoft Active Accessibility API, its use on getting properties from the Windows Live Messenger controls, and accessibility event hooking theory and practice, combined with an introduction to the COM interfaces exposed by the Messenger and how to use them to obtain contact information.

How to build and use the source

Note: 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:

  1. Open the wlmplugindemo solution on Visual Studio.
  2. Build the solution in Debug or Release mode, both should work.
  3. In the output directory, you'll encounter the three generated DLLs. Shutdown any msnmsgr.exe process in your system, and copy the three DLLs to the Windows Live Messenger application directory.

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 DLLs

You 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 caution

The 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 space

To 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 GradientFill.

Windows Live Messenger imports

We 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 LoadLibrary, either using the default Operating System library search order, or overriding it using absolute paths.

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 details

Our 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:

  • Load the MSIMG32.DLL system library from the Windows system directory with LoadLibrary.
  • Get the addresses of the to-be-forwarded functions with the GetProcAddress function.
  • Store the address of each MSIMG32.DLL function in function pointer variables.
  • Load the DLL to be injected with LoadLibrary. If this succeeds, we've successfully injected our DLL code in the msnmsgr.exe address space.
  • Get the address of the initialization function of the injected DLL (in our demo, named InitDLL) using GetProcAddress.
  • Jump to the initialization code of the injected DLL through the function pointer.

Note that using LoadLibrary in the DLL_PROCESS_ATTACH case of the DllMain switch sentence can lead to loader deadlocks. In this case, we're not leading the NT loader to make circular references, so we are safe, but always be careful with the code you're executing at DllMain.

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 DllInitialize and vSetDdrawflag, this is not evident since they're not officially documented.

Searching on MSDN about DllInitialize, we can find a description of the function, but it's clear that the particular MSIMG32.DLL DllInitialize is not the official DllInitialize: the MSDN documentation states that Export drivers must provide DllInitialize routines (see DllInitialize on MSDN). Are we dealing with a Kernel-mode driver DLL here? Definitely not, because kernel drivers must provide a DriverEntry export symbol, which is absent in MSIMG32.DLL.

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 __stdcall, which is the calling convention used by the Windows API. Also, it's very interesting for our research to note that vSetDdrawFlag is a C++ function. Utilities such as DUMPBIN can undecorate the name for us, giving the function signature we need:

void vSetDdrawFlag(void);

Now, DllInitialize is the remaining function we need to take into consideration. The _DllInitialize@12 name decoration says that DllInitialize uses a total of 12-bytes for its parameters. If we correctly assume that since this is a 32-bit DLL running in a 32-bit environment, all data is aligned on a 4-byte boundary, DllInitialize takes three 32-bit parameters from its caller. A preliminary declaration of this function surely can be:

UNKNOWN_RETURN_TYPE DllInitialize (DWORD p1, DWORD p2, DWORD p3);

We're left with the return type -- we know this is a __stdcall; therefore, the EAX register is used as the placeholder for returned values. A quick disassembly inspection of the DllInitialize function shows us that in fact the function is returning a value through AL before procedure return through RET. The listing below was captured while using PEBrowse Professional, an excellent Portable Executable dissection utility available for free at the developer website, SmidgeonSoft.

Now, with this information on hand, we can write down the final declaration of the DllInitialize function:

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)

DllInitialize is the entry point of the DLL, which is by default DllMain, but can be changed. A DLL entry-point routine has the following prototype, as stated in MSDN:

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 DllInitialize was right (in fact, from the microprocessor viewpoint, they are exactly the same), but for the sake of more precision, we finally rewrite our declaration as:

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 WINAPI calling convention (an alias for __stdcall) along with undecorated names using extern "C", even the C++ function vSetDdrawflag -- however, this function is void and parameter-less, so we can safely export it __stdcall and undecorated.

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 PRIVATE prevents the entry name from being placed in the import library generated by LINK.

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 AlphaBlend:

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 hacking

After 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:

  • Adding menus to the WLM main window
  • Adding toolbar buttons to the WLM window
  • Getting the contact list selection
  • Getting information for a selected contact

Prior to this, let's see a brief but necessary description of Trappola, the API hooking library from Nektra.

Introduction to hooking with Trappola

Trappola 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 hook

If you look at the hooking.h header in the wlmplugDLL project, you'll find HookArray[] which is an array of HOOK_DESCRIPTOR structures. As its name implies, each HOOK_DESCRIPTOR defines the properties of a specific interception to be done by Trappola. The members of the HOOK_DESCRIPTOR structure and their meanings are resumed on the following table.

Structure Member Meaning
szTargetModule The module where the function to be intercepted resides (e.g., USER32.DLL).
szTargetFunction The function in szTargetModule we want to intercept (e.g., CreateWindowEx).
cbParam The total byte count for parameters passed to the function, 4-bytes multiplied by the number of parameters in 32-bit environments. An important thing to remark is that all parameters must be specified, even the implicit ones, e.g.: this pointers in C++ member functions (excluding static members, of course) that are silently passed through the ECX register.
pvHookHandler Pointer to the entry point address of the hook handler. When this handler is executed is defined by the flags member.
nccCallingCnv A value from the NktCallingConvention enumeration specifying the calling convention of the intercepted function. See the enums.h Trappola header for possible values. Remember most of the standard Windows API is stdcall, while new components from later versions may differ (C++ or COM interface exports).
iFlags A set of flags defining the interception mode. In this project, it's enough to use _call_after (the hook handler is executed after the intercepted function is done, so return values can be checked). _call_before semantics are analog. Check the Trappola source for possible values.

At this point, you should be able to understand each of the array entries of HookArray[]. In the next section, we are going to hook CreateWindowEx to add a menu entry to the WLM top level window.

Hook attach and detach

A hook becomes active when a NktApiHook object is constructed with two NktFunctionWrapper functions as parameters. The NktFunctionWrapper objects encapsulate the hook handler function and the function to be intercepted. The resultant NktApiHook object is inserted into a std::list which contains ApiHookPtr objects. In turn, ApiHookPtr pointers are reference-counted pointers (a.k.a. 'smart-pointers') from the Yasper library, available at Sourceforge.

In our project code, you can see how the hooks defined in HookArray[] are 'activated', specifically in the AttachHook and AttachHookArray functions defined in the hooking.cpp file.

Disconnecting a hook just involves removing the respective NktApiHook object from the hook list (g_HookList global in this project code). When our plug-in DLL is being unloaded as the Operating System leads to call the DLL_DETACH_PROCESS switch case in dllentry.cpp, all hooks are removed with a simple g_HookList.clear() sentence.

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 classes

The 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 DirectUIHWND window class does not contain any child controls, at least none of the Windows API ones. So, how is it that those fancy UI elements are present on the WLM client area? By means of DirectUI, an undocumented framework that seems to be similar to XAML, the XML-based UI description format used in the Windows Presentation Foundation. In the resource hooking section in this article, we'll see that many XML elements used by DirectUI are similar to the XAML tags. Anyway, we are not going to execute DirectUI functions directly, but modify the resources read by the DirectUI parser.

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 MSBLWindowClass, and this is where we modify the menu.

Modifying the Windows Live Messenger menu bar

In 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 CreateWindowEx. Here, prior to hooking the API, it's necessary to clarify some concepts about ANSI and Unicode functions, because textually hooking CreateWindowEx won't work. And, this is applicable to most Win32 API functions.

About ANSI and Unicode APIs

In 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 GetProcAddress, for the reason that all tables on PE executables are encoded with ASCIIZ always). We were talking before about the CreateWindowEx function; is this call expecting ANSI or Unicode strings as input? The answer lies on the Windows API header macro:

#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 TCHAR* as your string type: Windows header macros will translate your TCHARs to wchar_t* (Unicode) strings, or to char* in case you are compiling to ANSI. Microsoft documentation is very clear on this topic, you can search it for a detailed explanation on Unicode and ANSI based applications.

Our case is explicit and clear: the Windows Live Messenger code is 100% Unicode. With this fact, we'll go into hooking CreateWindowsExW since this is the real name that the USER32.DLL library exports.

Intercepting and subclassing

As we've seen on the Trappola overview section, a HOOK_DESCRIPTOR type entry needs to be present in the HookArray[] in hooking.h. The required descriptor entry is as follows:

L"user32.dll", "CreateWindowExW", NUM_PARAMS(12), 
               Handle_CreateWindowExW, stdcall_, _call_after

The NUM_PARAMS(x) macro simply does 12 multiplied by the size of the current platform pointer. With the above entry specified, the prototype of the handler function can be created using the DECLARE_HOOK macro, or manually. For now, just remember the required signature for a Trappola function handler is:

void fn_Handler (NktHandlerParams* hp);

Don't worry now about NktHandlerParams, the practical use of this structure will be progressively devised in the article. A last necessary note about hooking the descriptor is that for a C++ function interception to be successful, the decorated name must be used. Therefore, if the module you want to target exports a C++ function with, e.g., the name Initialize@MyClass@@XYZXYZClass1@Object1@@@Z, you should specify such a string as the target function to hook, not the undecorated name that utilities like DependencyWalker may show (e.g., MyClass::Initialize(Class1::Object1*) for the example given here).

Our approach to create the menu will be to intercept CreateWindowExW. If the requested window class corresponds to the top-level window of Live Messenger (MSBLWindowClass), we subclass the created window to change the window procedure so we can process the messages we want, in particular WM_SHOWWINDOW. You may want to learn about window subclassing, the MSDN page Using Window Procedures explains all you need to know.

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.

  • The PARAMETER_INDEX(P) macro defined in hooking.h returns the parameter value at position P in a function handler. The PARAMETER_INDEX(p) macro is expanded by the preprocessor to ((size_t)hp->context.pms + (sizeof(DWORD) * (x))); thus hp->context.pms returns an array of 32-bit values with the function parameter values -- in turn, at the address pointed by hp->context.pms+32*N, you can find the Nth parameter value in a 32-bit system (at the time of writing this article, Trappola does not support 64-bit platforms). As you would expect with such a generic function, PARAMETER_INDEX returns void*, so a proper cast is necessary.
  • In the handler shown, we need the second (index 1) parameter from CreateWindowEx, which is a LPCWSTR-string type and indicates the class name of the window to be created.

  • The first if {} checks for one of the many weird things found on the Windows API. You can usually expect a class name to be a string, but this not the case. According to Microsoft documentation, the class name parameter passed to CreateWindowEx can be either an ANSI or Unicode string pointer, specifying a literal class name or an ATOM value. An ATOM specifies a value that maps to a literal string (the class name, in this case), but it's not a string. In fact, an ATOM is a 16-bit value, so the HIWORD is zero. If you don't check this, you may intercept a window creation with an ATOM as a parameter -- interpreting this as a string pointer will crash your program since the 0x0000-0xffff address range is not accessible for any user-mode process. Therefore, according to our analysis, the if block gets executed only if the window class name parameter of the intercepted CreateWindowEx represents a valid string pointer address.
  • The comparison on the inner if returns true if the window being created is the top level WLM window, whose class name is stored on the global identifier g_topWlmWindow.szClassName. This variable is an instance of the WLM_TOPWINDOW structure, used to store values about this window such as class name and handles.
  • When CreateWindowEx successfully creates a window, it returns a window handle (HWND) value for that window. We store this value on g_topWlmWindow.hwnd, taking it from the actual return value, inside the function hook handler. How can this be? Remember this function is being hooked with the _call_after flag, therefore CreateWindowExW has already returned a value when the Handle_CreateWindowExW execution begins; and because this is a Windows API call, return values are passed to the caller through the EAX register.
  • You can see this is another use of the NktHandlerParams context member. hp->context->regs gives you the possibility of accessing the x86 registers. E.g.: if you were hooking a C++ non-static class member function, hp->context->regs.ECX could give you the this pointer address.

  • Finally, with the GetWindowLongPtr/SetWindowLongPtr pair in the last two lines, we subclass the top level Windows Live Messenger window by specifying our own window procedure. Of course, the old window procedure address must be saved because if our window procedure does not handle a particular message, it should let the original window procedure handle it. This technique allows 'extensible' window procedures in a pseudo-OOP-like manner, where you override just the message handling you want and let the 'parent' (old) procedure to handle the rest.

Therefore, when CreateWindowExW for the top-level window gets called, our hook handler stores the HWND for future reference, and subclasses the window. What we want to do in the subclassed window procedure is to add an element to the menu bar when the window gets shown, and preferably once and forever.

We must examine what window message to process to do this. WM_SHOWWINDOW actually works since in the Windows Live Messenger, it gets called once when the window is displayed the first time (this behavior depends on some conditions, see WM_SHOWWINDOW Notification for more information). The code where the a menu element is added is as follows:

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 GetMenu, create a new popup menu with CreatePopupMenu, and through this handle, we append the entries.

The remaining task is to catch WM_COMMAND to handle our menu clicks. This is done in the Win32 API standard way. The code skeleton could be something like:

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 g_wlmTopWindow.pfnOldWndProc. To the old window procedure, the Windows API provides the CallWindowProc function, so the default case for message handling in a window procedure switch..case scope should be:

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 g_wlmTopWindow.pfnOldWndProc. You can see the details in the wndhook.cpp source file.

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 subclassing

If 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 NKT_COLORIZED_WINDOW_CLASS. The name tells one of the properties that are inherited by windows based on this class: the window gets "colorized" by sampling a pixel color from the WLM top window client area, apart from getting a rounded shape using a round-rectangle region. Keep in mind that we won't give all the details about, e.g., how the gradient fill is built for the window background, but instead, the general structure for the approach.

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: WM_CREATE, WM_ERASEBKGND, WM_DESTROY, WM_NCHITTEST, and the application-defined WM_UPDATEBASECOLOR.

When the window class is registered through RegisterClassEx in the RegisterColorizedWndClass function, you can see that all windows will drop a shadow with the class style CS_DROPSHADOW; there is no background defined, so the WM_ERASEBKGND code will draw the background, and a number of extra bytes for the window class are defined with the member cbClsExtra of the WNDCLASSEX structure. In many Windows API applications, you will find this member set to zero. But remember that, we want all windows based on this class to share a common background color, so this visual aspect is class-dependent, not window-dependent. Instead of using a global variable, we are going to store this color information in the window class itself: that's the reason we are reserving the number of bytes needed to store an RGBA-color (which is the size of a COLORREF). A last word on class extra bytes is that a maximum of 40 bytes can be allocated, so the rule of thumb is to store a pointer if you want to refer to bigger data structures.

Now, both windows ('About' and 'Contact Information') are created the same way. They define the window-specific properties they need, but specify NKT_COLORIZED_WINDOW_CLASS as their class:

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 WM_CREATECHILDCTRLS message is defined by our application (as WM_USER+100) to create the child controls and load the logo bitmap; and, you may ask why we didn't do those duties directly when the WM_CREATE message is sent by Windows to the callback. The reason is that you can't. When the About window is created, it's defined to be based on the Colorized window class -- this means that the WM_CREATE message for the Colorized window procedure is the only one that gets called, because you are subclassing after the window is created. However, it's possible to change the window procedure of the Colorized window class using SetClassLongPtr before the window is created, although this is not recommended since it can affect the remaining windows based on the class.

So, according to our code, after we create the About window, we can call SendMessage with WM_CREATECHILDCTRLS to setup our window. The WM_COMMAND and WM_PAINT are "specialized" by our window, since the Colorized window procedure passes those messages to the default Windows processing call DefWindowProc. The interesting message here is WM_CLOSE: this message is also passed to DefWindowProc in the window procedure of our "parent" window class, and this means that, by default, Windows will call DestroyWindow when e.g., Alt+F4 is pressed with this window activated. We don't want this to happen because if the window gets destroyed, we must create it again, which is unnecessary; therefore, we change the code of WM_CLOSE handling to "simulate" the click on the OK button, which, in effect, hides the window. We do this through a simple SendMessage 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 CallWindowProc function. Be aware to not pass them to DefWindowProc: this is incorrect since none of the base class messages will be called.

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 WM_NCHITTEST message since they are deprived from a title bar. But, we don't want the contact information window to be movable, because it's "attached" to the main Windows Live Messenger window. How can we achieve this? Simply by "rejecting" the WM_NCHITTEST message in the Contact Info window procedure, with these simple lines:

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 resources

In 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 stored

The resources used by the Windows Live Messenger can be classified as,

  • Standard Win32 application resources such as dialogs, menus, icons, bitmaps, strings, etc., that you may insert in your native Win32 application using, e.g., the C API or MFC.
  • Resources that are managed by the internal DirectUI framework and described by XML.

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.

ID Description
919 MSN Today, Tab Content, Pending Requests
920 Conversation Window
921 Toast Window
922 Group Conversation Container
923 Contact List
926 Standard Message Box
930 Activities Menu, My Games
931 Contact Card
932 Activities Menu, My Activities
934 Emoticons Dialog
935 Winks Dialog
937 Download MSN Messenger Content, "Single Click Dialog"
940 Content Drop Down Menus
941 Dynamic Display Picture Container
942 Automatic Background Share Dialog
943 Connection Test Dialog
944 Flash Upgrade Dialog
945 Ink, Video, Send File, and Call Drop Down Menus
946 Backgrounds Dialog
947 Login Window
949 File Sharing Window
950 File Sharing History
952 Templates related to toasts (further research required)
953 Dangerous File Transfer Dialog
1001 Address Book Toolbar (The one with the word wheel, Add Contact, and Manage Contacts button)
1004 Detailed Contact List
1009 Normal Contact List
44101 Contact Picker Dialog
44102 Well Address Templates
44103 More word wheel related elements
44104 Address Book Templates (Buddy, BuddyPickerExpanded, Group, Error)
45700 Mobile Selector
45701 Edit Group Dialog
45703 People Picker
45704 Additional address-book templates
45705 Mobile Address Selector
45710 Add/Edit Contact Dialog

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 sequence

The 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:

Function Semantics
HRSRC FindResource(HMODULE, LPCTSTR, LPCTSTR) Determines the location of a resource with the specified type and name in the specified module.
HGLOBAL LoadResource(HMODULE, HRSRC) Loads the specified resource into global memory.
LPVOID LockResource(HGLOBAL) Locks the specified resource in memory, returning a pointer to the first byte of the resource.
DWORD SizeofResource (HMODULE, HRSRC) Returns the size in bytes of the specified resource.

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 FindResource -> LoadResource -> LockResource are the required steps to successfully obtain a valid pointer to the resource. However, the resource size can be obtained between any of the calls, but always after FindResource has returned a valid HRSRC handle, as is denoted by the SizeofResource input parameter type. This general sequence of calls, which is regularly used in Live Messenger, can be represented in the following graph:

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>..</MToolbar> tag pair, and it's not a lot of code. It's presented below with two button definitions in case you haven't explored the resource DLL yourself yet.

<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, id=atom(X) is related to the stylesheets found on the resource type #4005, where a matching ID x style is applied to the UI element. For example, the first element ID we see here is ToolbarScrollLeft, which is very likely to be the button to pan the toolbar to the left when the number of elements of the toolbar exceeds the rendering area and the user has scrolled the toolbar to the right, hiding the leftmost elements. The style of this scroll button can be found at the resource 4005:923, with the following style definition:

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 rcXXX(RID) keyword to indicate that the definition of the property is found on another resource. E.g., the first line defines a padding rectangle for the element which is found at the resource ID 20213. In the WLM stylesheet resources, you can find the following rcxxx references, along with the kind of resource it represents and the place where you can find the referenced resource ID.

Keyword Resource kind Location
rcstr String String table resource on the language-dependent DLL msgslang.[version].dll
rcimg Bitmap (RLE/PNG) Resource type #4000
rcint 32-bit Integer value Resource type #4002
rcrect Rectangle (15-byte value) Resource type #4003
rcclr 32-bit RGBA color Resource type #4002
rcbkd Background (RLE/PNG) Resource type #4000

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 class

Returning 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 tables

The resource manager class (from now on, RMC) maintains three tables, based on std::map, for tracking resource hooking activities: the registered resource table, the resource handler table, and the resource pointer table. All table entries are stored as pairs, specifically std::pair objects. The list below enumerates the purpose and general structure of each table.

  • The registered resource table (declared as REGISTERED_RESOURCE_TABLE) is the main table where all resources we want to be managed by the RMC are stored. The first pair element of each entry is an ULONGLONG value (64-bit unsigned) to keep the resource ID and the type together. The macro MAKEULONGLONG in utility.h is used to build a 64-bit unsigned value from two 32-bit double words. The second pair element is an instance of the RESOURCE_MOD_DESCRIPTOR structure which describes the properties of this resource entry. The meanings of this structure's members will be explained later.
  • The second table called the resource handler table (RESOURCE_HANDLE_TABLE) stores values returned by the FindResource API (HRSRCs values), along with the ULONGLONG value which acts as a pointer to the register resource table matching entry. The purpose of this table is to feed HRSRC handles to the LoadResource function, in case this function is trying to load a resource indented to be hooked. If this is the case and LoadResource can successfully return us a HGLOBAL, this returned handle is stored on the following table.
  • Finally, the third table, and the most important for our needs, is the resource pointer table (RESOURCE_POINTER_TABLE) where memory addresses to the resources are found. For each entry, this table contains a pair for which the first element is the HGLOBAL returned by a successful LoadResource call, and the second element is a sub-pair with the 64-bit unsigned value representing the registered resource as the first element and the resource address returned by LockResource as the second element.

A key concept is that our hooking work tries to respect the semantics of the original (unhooked) APIs, e.g., the LockResource handler is the only one that deals with memory addresses, and where you can get either unhooked resource addresses, or addresses pointing to new allocated resources. We've also tried to minimize calls between the APIs, trying to isolate them. This was achieved partially due to the fact that the SizeofResource function can be called at any point after FindResource returns a valid HRSRC, as we've seen in the previous section.

The internal tables of the RMC and their general operations can be seen in the following schema (values are fictitious):

Built-in and external resources

RMC 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 MToolbar tag pair; however, the interesting question is selecting a bitmap for the new button in the related stylesheet with rcimg(). This bitmap can refer to an existing bitmap resource, and this does not pose major problems. But, what about using our own button image? The approximation of RMC is to allow the specification of new resource IDs that refer to an external DLL (resdll.dll). The general idea is that FindResource will try to load a resource that does not exist in the WLM resource DLL (since we are specifying a non-existing ID); in this case, if this request effectively refers to our resource, the RMC will eventually return a valid address for a resource that is not in the WLM resource DLL, but in our own DLL instead.

Registering resources

In 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, CResourceManager: RegisterResource and RegisterNewResource. The prototype of the first function and a description of each parameter follows.

void CResourceManager::RegisterResource (DWORD dwType, DWORD dwName, LPVOID pvResData, 
                                         DWORD cbResDataSize, UINT iResModType, 
                                         UINT cbWhere = 0);
Parameter Usage
dwType, dwName Specifies the resource type and name ID. This must point to an existing WLM resource.
pvResData A pointer to the first byte of the resource data to be added, inserted, appended, or replaced-by for the specified resource.
cbResDataSize Byte count for the data pointed by pvResData.
iResModType A symbolic constant specifying the operation to be done between the original resource and the data pointed by pvResData. Valid constants are:
RR_APPEND Data is appended to the end of the original resource.
RR_INSERT Data is inserted into the resource at the byte position indicated by the cbWhere parameter.
RR_REPLACE Original resource is totally replaced by new data.
RR_COPY or RR_NONE No modification is done to the resource: original resource data is copied to a new allocated virtual address.
cbWhere Count of bytes before the position of an insertion. This is zero by default, and is valid only when RR_INSERT is specified in the iResModType parameter.

The above function is used for registering existing resources only. For using resources from the external resource DLL resdll.dll, the RegisterNewResource function is provided by the RMC.

void CResourceManager::RegisterNewResource (DWORD dwType, DWORD dwName, 
                                            const RESOURCEINFO& ri);
Parameter Usage
dwType, dwName Specifies the resource type and name ID. The resource type can be, e.g., 4000 for WLM bitmaps, while the name ID can be any value not used in the WLM resources (e.g. > 60000)
ri A reference to a RESOURCEINFO structure specifying the properties of the external DLL contained in the resource to be used. A RESOURCEINFO structure has the following members:
uResId Resource ID from the provided DLL.
wszResType Resource-type string for the resource. This type parameter can be converted to an integer using the MAKEINTRESOURCE macro.
hrsrc HRSRC handle obtained from FindResource.
dwSize Size of the resource, in bytes, returned by SizeofResource.
hResData Data handle returned by LoadResource.
dwSize Address to resource returned by LockResource.

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 example

In 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 WLM_TOOLBARBUTTON structures, which defines the appearance and behaviour of the buttons. The WLM_TOOLBARBUTTON contains the following members declared in the wlmplugin.h header file:

WLM_TOOLBARBUTTON member Usage
fExtBitmap If true, the bitmap for the button image comes from an external DLL such as the included resdll.dll; otherwise, the resource is located at type ID #4000 in the Windows Live Messenger resource file msgsres.dll.
hModule The module handle to the external DLL if fExtBitmap is true; NULL otherwise.
wszResType Resource type string in the resource DLL, e.g., "PNG", "RLE", "BITMAP". The easy way to provide additional bitmaps is to encode them in 24-bit PNG, as they are loaded and displayed without problems by the DirectUI code.
uResID Resource name ID from the external DLL if fBitmapExt is true. If the resource is located on the WLM resource DLL, the uResID value must exist for a resource name #4000.
uPosition Where the button is to be positioned in the toolbar. Acceptable values are:
  • An absolute byte position into the resource data to be modified. E.g., 0x1e05 will put the button at the last position for Windows Live Messenger 8.5 (see a hex dump of the msgsres.dll resource 4004:923 with a resource inspect tool).
  • The constant TB_INSERT_FIRST to insert the button at the front of the toolbar.
szButtonId Unique button ID ANSI string for UI XML definition resource and XML stylesheet.
wszButtonID Wide-char string version of szButtonId for functions requiring Unicode string, avoids conversion functions.
szTooltip ANSI tooltip string to be displayed when hovering over the button with the mouse.
pfnAct Pointer to the function to be executed when the button is pressed. A function compatible with the PFNBUTTONPROC type must conform to the signature:
void function_name (void *)
The void* parameter is optional, and is meant to pass user-defined data.

Following the WLM_TOOLBARBUTTON array definition, we fill the structure members with data. As you can infer looking at the dllentry.cpp code, the buttons get the following properties:

  • Both buttons will be inserted at the front of the toolbar.
  • Bitmap resources are external. IDs for those resources are IDB_MPLAYER and IDB_NKT.
  • They execute mplayrun and webnkt, respectively, when pressed on.

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 AddToolbarButtons function takes the WLM_TOOLBARBUTTON array and the number of elements it contains, along with pointers and the size of both buffers. When this call returns, hooking is activated to intercept Windows Live Messenger calls with AttachHookArray.

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);

uLastRcImg is incremented for each button addition that uses external resources from a DLL, and it's a high enough value not probable to clash with the existing bitmap resource IDs. uImgId stores the final resource ID depending on whether an external bitmap is used, and dwBytePos is the absolute byte aligned position where the XML button definition is to be inserted.

The main work is done inside the for loop. For each iteration:

  • A XML user interface element resource is defined and copied into the tmpRes character array.
  • A matching style definition is generated with proper fields for the button to be able to execute its default action. This is related to the buttons supporting Microsoft Active Accessibility, which we're going to explain later. Of course, button IDs and tooltip text are also written out to the tmpStyle array.
  • In the style definition, the rcimg property is set to point to an internal WLM resource, or to the custom uLastRcImg ID for external DLL provided resources.
  • If the bitmap image comes from an external DLL (fExtBitmap is true), the resource management functions of the Windows API are called to obtain the necessary handles to the resource block and address. Remember that the hooks over those functions are attached when this function body exits, so we are safe to call them here. With this information obtained successfully, the RMC member function RegisterNewResource is called for the resource type 4000 corresponding to the WLM bitmaps, the generated ID for this new resource (uLastRcImg), and the filled RESOURCEINFO structure. This is only to register the new toolbar images, not the XML UI element or style resources.
  • The button click event is registered on the CActionDispatcher class instance using the button ID and the function pointer. There is not much to talk about this class; it tracks button actions with a std::map, so when a desired action is to be fired, the ExecuteAction member function is called, with optional user data passed through the void pointer for PFNBUTTONACTION function pointer types.

When all the resource data related to the new toolbar buttons is finally defined in szStyle and szXmlRes, it's registered by means of RegisterResource. The use of RegisterResource instead of RegisterNewResource is clear: we're modifying the existent resources, both the style sheet and the element definition XML. The relevant source lines are:

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 insertion positions are hardcoded according to the resources found on WLM 8.5. This is a limitation that can be overcome either by implementing at least a minimal XML parsing engine for the WLM resource data, or using conditional compilation by using the WLM_VERSION_MAJOR and WLM_VERSION_MINOR macros. Just be careful to refer to valid positions so you don't mess the XML structure. In the case of WLM 8.5, we determined the positions by dumping the needed resource data and looking at it with a hex viewer (such as FAR shown below):

Surrounded with a red box is the position (0x1524) where the buttons definition start in the resource 4005:923, after the opening MToolbar tag.

As we already know, the resource 4005:923 contains the related style definition; insertion can be safely done at position 23 (0x17), after the header:

<style resid=mainwndss>

After the two toolbar buttons are added and their resources registered, the state of the RMC registered resource table should be similar to the following (structure members are not shown):

REGISTERED_RESOURCE_TABLE
Type and Name ID RESOURCE_MOD_DESCRIPTOR
0x00000FA00000F618 RESOURCE_MOD_DESCRIPTOR members
0x00000FA00000F619 ...
0x00000FA40000039B ...
0x00000FA50000039B ...

The four 64-bit ULONGLONG values correspond to the two custom bitmaps (4000:63000 and 4000:63001), the XML UI definition of the buttons (4004:923), and the matching style (4005:923).

Resource management execution flow

With the registered resource table filled up, the program gets out from the AddToolbarButtons scope and attaches hooks to FindResource, LoadResource, LockResource, and SizeofResource. Now, let's see how we lead the WLM to use the proper resources to make our toolbars to successfully work. Keep in mind that the explanation is limited to the loading of one toolbar button; for details, refer to the CResourceManager class related files and reshook.cpp where the API hook handlers are defined.

  • Live Messenger parses the XML resource where the UI elements are defined; this is at resource 4004:923. Each resource ID that does not match an entry on the registered resource table is loaded without doing anything extra.
  • When the DirectUI parser in msncore.dll reaches our inserted button tags, it does a lookup in the style data at 4005:923 to find the corresponding style definitions for our buttons. It will encounter the rcimg keywords referring to bitmap resources with IDs 63000 and 63001, then calls FindResource to get the HRSRC handle for resources 4000:63000 and 4000:63001, which, of course, do not exist in the msgsres.dll resource DLL for the Windows Live Messenger. In this case, our FindResource hook handler knows that it's asking for a new resource, so it forces the API to return the HRSRC that points to the external bitmap we are providing, in addition to inserting it into the resource handle table.
  • Live Messenger continues its sequence of calls for loading the resource into memory. When LoadResource is executed, its hook handler follows the same logic we explained above: if a new resource is being loaded, it returns the HGLOBAL for the bitmap residing on the DLL. At last, it adds the HGLOBAL handle to the resource pointer table for our bitmap.
  • For the LockResource interception, the hook handler function calls CResourceManager::AllocResource which allocates the necessary virtual memory to store the new bitmap (in addition to inserting new resource data into an existing resource if the RR_INSERT modifier is used). Then LockResource returns the address of either the new resource copied from the external DLL, or the address where a modified resource exists now.
  • The case of SizeofResource hooking does not follow the above logic. Because SizeofResource may be called at any instant following a successful FindResource, for registered resources, we force the resource "pipeline" to always go through the sequence FindResource->SizeofResource. In any case, the hook handler of SizeofResource will always return an already stored value. When the SizeofResource handler figures that a call is being made for the first time, it returns the default, non-modified resource size which was in the RESOURCE_MOD_DESCRIPTOR instance at the registered resource table. Later calls may return the new modified resource size (e.g., the RR_COPY modifier will return the same size every time).

This is a very bare-bones depiction of how resources are managed; I encourage you to look carefully at the source code and, of course, apply the modifications or ideas you want to this scheme. To finish this exposition, a capture of the added buttons is shown:

In the next section, you'll learn how to handle the toolbar button click events through Active Accessibility.

Getting the contact list selection

This is our last dissertation, and involves how to get the currently selected contact information through the contact e-mail, which in fact is the unique identifier of an MSN contact. Note that to fully understand the exposition below, you need to know the basic theory of COM: querying interfaces, IUnknown pointers, class factories, and similar concepts. Remember that thanks to COM being essentially a binary component architecture, not a source-code based one, you can take this section as a base for your own ideas, and implement them in other languages that support COM, such as Visual Basic or Delphi.

An approximation to MS Active Accessibility (MSAA)

Active Accessibility is a Microsoft COM-based technology designed to develop software that supports accessibility features supported by the underlying Operating System components, so characteristics, for example, for visually impaired users can be added "naturally" to a Windows application. Therefore, the key objective of MSAA is to design software with accessible user interfaces, or to easily extend the current software to be able to support those accessible features.

You may ask what MSAA has to do with Live Messenger contacts. The answer comes from the fact that theWindows Live Messenger Direct-UI interface supports Active Accessibility, even when the owner-drawn controls are hidden from window spying utilities, so we're going to take this for peeking and hooking with those controls.

To begin with the MSAA topic and how it is related in practice with the Windows Live Messenger, we'll use the Accessibility Explorer tool found in the Accessibility SDK to peek into the area we are interested to inspect, the contact list.

The above screenshot shows the accessibility properties of a contact. Considering that every child control that supports accessibility (like the contact we are inspecting) can be accessed through a COM object, it's perfectly possible to get the contact ID using the proper interface. But, through what interface? The answer is simple: all applications with MSAA-based user interfaces implement IAccessible. For example, with a valid pointer to IAccessible, it's rather easy to get the state of the control with:

pIAcc->get_accState(CHILDID_SELF, &v);

The constant CHILDID_SELF indicates the method to return information for the control itself, instead for any of its children, while v is a VARIANT type variable containing the state of the object in the lVal member (vt variant member is VT_I4). Refer to MSDN for Object State Constants and VARIANT Structure.

With this quick start, but before going to the sample code, we are going to talk about the COM interfaces exposed by the Windows Live Messenger.

The Live Messenger type libraries

It is known that the Windows Live Messenger offers many COM interfaces that can be used directly with Visual Studio by using the #import directive pointing to the WLM executable type libraries. Those interfaces are quite useful, and are enough to code pretty good plug-ins for the most popular messaging application; there is one caveat, however: working with those interfaces will make the Windows Messenger msmsgs.exe executable to start, because the default type library points to the Windows Messenger relay object. Since the Windows Messenger is not available anymore on post-XP systems such as Vista, objects won't be created from the default type library. This is totally undesirable.

The solution is to use a customized type library made by a well-known user of the Fanatic Live Forums with the nickname 'TheSteve'; a custom TLB with the particular feature of ignoring the Windows Messenger process. According to the posts there, the wonderful type library created by this user works on the latest WLM 9.0 betas as well. For more information, see the thread MessengerAPI without Windows Messenger. You will find the MSNMessengerAPI.tlb type library in the tlbs directory.

Now, to get a pointer to a MSNMessenger object instance, we could hook CoCreateInstance as it's the common function to create COM objects. Unfortunately, it does not work for catching the creation of the object; however, we can successfully intercept the creation of the object instance by hooking CoRegisterClassObject instead. The relevant handler is in the comhook.cpp source file:

void Handle_CoRegisterClassObject (NktHandlerParams * hp)
{    
    HRESULT            hr;
    WCHAR            wszBuf [256];
    CLSID            clsid = **(CLSID**)PARAMETER_INDEX(0);
    IUnknown*        pUnk = *(IUnknown**) PARAMETER_INDEX(1);
    IClassFactory*    pIcf = NULL;

    if (SUCCEEDED((HRESULT)WINAPI_RETVAL))
    {
        StringFromGUID(clsid, wszBuf);
        _OutputDebugString (L"S_OK, 
           Handling CoRegisterClassObject for CLSID %s", wszBuf);    

        // check CLSID ...
        if (clsid == MSNMessenger::CLSID_Messenger)
        {
            OutputDebugString (L"Handling CoRegisterClassObject for CLSID_Messenger\n");
            // get ptr to class factory
            hr = pUnk->QueryInterface(IID_IClassFactory, (void**)&pIcf);
            if (SUCCEEDED(hr))
            {
                hr = pIcf->CreateInstance (NULL, MSNMessenger::IID_IMSNMessenger, 
                    (void**) &g_wlmIfaces.pIMsn);

                _ASSERTE(hr == S_OK);                
            }            
            pUnk->Release();

            // attach hook to handle MSAA events
            g_wlmTopWindow.hwevh = SetWinEventHook(EVENT_OBJECT_FOCUS, 
                EVENT_OBJECT_STATECHANGE, GetModuleHandle(0), 
                &HandleWinEvent, GetCurrentProcessId(), NULL, 
                WINEVENT_INCONTEXT);
        }
    } 
}

The logic is easy to understand. For every successful CoRegisterClassObject call, we check if it's related to the CLSID of the desired MSNMessenger coclass. When we catch it, we create an instance of the object through a class factory interface (IID_IClassFactory) storing the IMSNMessenger interface pointer into the member pIMsn of a WLM_IFACES structure.

Once we properly get the IMSNMessenger interface pointer, we proceed to hook Active Accessibility events with the purpose of catching the state and focus change on the contact list items. We use the MSAA provided hooking API SetWinEventHook instead of Trappola, because Windows Live Messenger DirectUI controls don't work with the standard Win32 API message callback mechanism, therefore the subclassing method we reviewed earlier does not apply. But thanks to MSAA, all controls "emit" messages that can be intercepted with this MSAA hooking capability.

Catching accessibility events

We are going to get the contact information when the user changes the selection state of a contact. The easiest way to do it is to handle the focus change event EVENT_OBJECT_FOCUS, filtering focus events for controls different from a contact list user. Regarding the click events of our added toolbar buttons, we handle EVENT_OBJECT_STATECHANGE, filtering "Pressed" state changes only for buttons. When a button is clicked, we call the CActionDispatcher::ExecuteAction method to tell the action dispatcher class to execute the proper function if the button ID is registered; for the remaining default buttons, the Live Messenger obviously does nothing. The code above is the MSAA event handler which was specified with the SetWinEventHook API function.

void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
                                  LONG idObject, LONG idChild,
                                  DWORD dwEventThread, DWORD dwmsEventTime)
{
    IAccessible* pIAcc = 0;
    MSNMessenger::IMSNMessengerContact* pIMsnContact = 0;
    VARIANT varChild, varRole, varState;
    BSTR name = NULL;
    WCHAR wszID[MAXSTRL];

    VariantInit(&varChild);
    HRESULT hr = AccessibleObjectFromEvent (hwnd, idObject, idChild, &pIAcc, &varChild);

    if ((hr == S_OK) && (pIAcc != NULL)) 
    {
        if (pIAcc->get_accName(varChild, &name) != S_FALSE)
        {
            switch (event)
            {
            case EVENT_OBJECT_FOCUS:
                // process only if the contact info window menu option is enabled

                if (AccObjToContactID(name, wszID) && g_wlmTopWindow.fEnableInfoWnd)
                {    
                    BSTR bstrID = SysAllocString(wszID);                    
                    IDispatch* pIDisp;
                    hr = g_wlmIfaces.pIMsn->raw_GetContact(bstrID, 
                         g_wlmIfaces.pIMsn->MyServiceId, &pIDisp);
                    
                    if (SUCCEEDED(hr))
                    {
                        if (SUCCEEDED(pIDisp->QueryInterface(
                            MSNMessenger::IID_IMSNMessengerContact, 
                            (void**)&pIMsnContact)))
                        {
                            ShowWindow(g_wlmContactInfoWindow.hwnd, SW_SHOWNA);

                            // force window to repaint with new values
                            g_wlmContactInfoWindow.wszContactID = bstrID;
                            g_wlmContactInfoWindow.wszFriendlyName 
                              = pIMsnContact->FriendlyName;
                            g_wlmContactInfoWindow.msStatus = pIMsnContact->Status;
                            g_wlmContactInfoWindow.fBlocked = pIMsnContact->Blocked;
                            g_wlmContactInfoWindow.fCanPage = pIMsnContact->CanPage;
                            g_wlmContactInfoWindow.wszHomePhone 
                              = pIMsnContact->GetPhoneNumber(
                                MSNMessenger::MPHONE_TYPE_HOME);
                            g_wlmContactInfoWindow.wszWorkPhone
                              = pIMsnContact->GetPhoneNumber(
                                MSNMessenger::MPHONE_TYPE_WORK);
                            g_wlmContactInfoWindow.wszMobilePhone
                              = pIMsnContact->GetPhoneNumber(
                                MSNMessenger::MPHONE_TYPE_MOBILE);

                            SendMessage(g_wlmContactInfoWindow.hwnd, 
                                        WM_UPDATEBASECOLOR, 0, 0);
                            pIMsnContact->Release();
                        }
                        pIDisp->Release();
                    }
                    SysFreeString(bstrID);
                }    
                else // if it's focused to a non-contact element...
                {
                    // Hide contact info window
                    ShowWindow(g_wlmContactInfoWindow.hwnd, SW_HIDE);
                }
                break;

            case EVENT_OBJECT_STATECHANGE:
                // We handle this for toolbar buttons going to "pressed" state
                varRole.vt = VT_I4;
                pIAcc->get_accRole(varChild, &varRole);
                pIAcc->get_accState(varChild, &varState);
                if (varRole.lVal == ROLE_SYSTEM_BUTTONMENU)
                {            
                    if (varState.lVal == STATE_SYSTEM_PRESSED)
                    // Execute action related to this button name
                    g_actDisp.ExecuteAction(std::wstring(name), NULL);
                }
                break;
            }

            SysFreeString(name);
        }
        pIAcc->Release();
    }    
    VariantClear(&varChild);
}

Do not look deeply at the details, let's just analyze the logic of the MSAA event handler:

  • When the SetWinEventHook was called, the range of events to be handled was limited to the range EVENT_OBJECT_FOCUS..EVENT_OBJECT_STATECHANGE (see the CoRegisterClassObject hook handler in the previous section). So, when such events occur, we first get the accessible object emitting the message with AccessibleObjectFromEvent. If the object name is valid, we store it in the name BSTR-type variable.
  • For a focus change event, we validate and extract the user sign-in name (ID) from the IAccessible::get_accName property. After this, contact information is retrieved through the IMSNMessenger interface pointer, which is in turn obtained from the IDispatch pointer since IMSNMessenger is a dual interface. This kind of interfaces allow clients to access its methods through a dispatch interface or the standard vtable mechanism, and is aimed at COM client development on languages not supporting calls through vtable, such as Visual Basic 6.
  • For a state change event, we check if both the Active Accessibility role property is ROLE_SYSTEM_BUTTONMENU and the state went to pressed (STATE_SYSTEM_PRESSED). If this condition is true, then proper action is executed through the Action Dispatcher class.

This completes our explanation of Active Accessibility in particular, and the review of the techniques for the article in general.

Final words

I hope you enjoyed the article and the code. Consider this dissertation as a base document in the field of reverse engineering and plug-in development for the Windows Live Messenger, as there are many techniques and ways to accomplish what we've done. As I've said at the start, the ideas applied can be aimed generally at any Windows application.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

About the Author

IndioX86
Software Developer
Argentina Argentina
C/C++ developer interested on operating systems, reverse engineering, general system programming, CPU architecture, etc.
Now I'm working creating plugins Outlook Plugin Development.

Comments and Discussions

 
QuestionHow does the code run? PinmemberMember 1026167111-Sep-13 0:01 
QuestionGreat article! Pinmemberrosone20-Oct-11 21:47 
QuestionGood ! PinmemberKoma Wang21-Aug-11 19:18 
GeneralMy vote of 5 Pinmemberring_024-Jul-11 20:58 
GeneralMy vote of 5 PinmvpAjay Vijayvargiya18-Jan-11 16:07 
GeneralCompatability with WLM version 14 Pinmemberhp0016-Sep-10 15:49 
GeneralRe: Compatability with WLM version 14 Pinmemberrosone20-Oct-11 21:44 
GeneralC# help required Pinmembershakeebgenii23-Jun-10 9:17 
Generalbad word filter to protect my kids. PinmemberZUPERKOOL7-Feb-10 6:21 
a warm hello to all msn gurus Smile | :)
 
hi i am trying to develop a badword filter for msn/messenger to help protect my kids from bad words... "yes those words ...that later i we have to make up a fake definition when they ask dad what is the meaning of f**k :x"
 
anyways, i started using winpcap to capture the incoming socket but later found out that since sp2 xp will not allowed this"
 
the big question:
 
while reading this article it came to my attention that maybe i could filter at the application level....
 
do you think if taking this/your aproche would eventually end up in a simple badword_filter.dll.
 
thanks
 

Ivo Gomez
http://ivogomez.com
 



GeneralHi,I found some problems. Pinmemberwxdwxd30-Jun-09 0:15 
GeneralRe: Hi,I found some problems. PinmemberMember 243873718-Jan-10 5:31 
Questionany clue on adding buttons to conversation windows of wlm 2009? PinmemberTony Siu13-Apr-09 20:21 
AnswerRe: any clue on adding buttons to conversation windows of wlm 2009? PinmemberAnson Ye15-Jun-09 17:47 
QuestionIsn't there a much (much, much) easier way to do this? Pinmemberjasdflkjasdfj26-Mar-09 10:35 
GeneralIs there a new method to modify MSN 9 resource file. Pinmemberrayshun16825-Mar-09 16:47 
Generalis this way works with live 9 Pinmembermascix19-Jan-09 4:40 
GeneralIt is what I need, Thank you! Pinmemberbruce_chenwm11-Jan-09 21:34 
QuestionHao about VC++6? Pinmemberredangell11-Jan-09 14:57 
AnswerRe: Hao about VC++6? PinmemberIndioX8611-Jan-09 15:55 
GeneralGood example of DLL injet to hook with other application PinmemberJ.Xing11-Dec-08 12:04 
GeneralRe: Good example of DLL injet to hook with other application PinmemberIndioX8611-Dec-08 16:30 
GeneralIt is what Hacking is about... Pinmemberflexluther06-Dec-08 7:10 
GeneralNeed help PinmemberStormofwar20-Nov-08 5:00 
GeneralRe: Need help PinmemberIndioX8620-Nov-08 5:54 
GeneralRe: Need help PinmemberStormofwar20-Nov-08 20:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 3 Nov 2008
Article Copyright 2008 by IndioX86
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid