The Windows Operating System provides us with fair amount of interprocess communication (IPC) options. A few of them, however, often are forgotten despite being very powerful and very easy to implement. This time we will turn our attention towards the Message Only Windows as a convenient way to exchange data between two processes.

Some time ago, we were asked to investigate a piece of software that allowed ripping DRM protected iTunes music from iTunes to another format, so that the files could be played on non-iTunes enabled devices. We are, of course, not going to cover the mechanism used to penetrate iTunes’ protections on neither Windows nor OS X, despite the fact that it could be an exciting read. The only aspect worth mentioning is that the mechanism comprised an executable and a dynamic library, which in turn was injected into the iTunes process.

You will agree if we say that there is nothing unusual this far. It was, however, interesting to see the way the injected library communicated with the executable. One would expect shared memory and a TCP connection to localhost, which is entirely standard practice. However, the software used a much less common [in such applications] method of control communication between the two – message-only window.

A message-only window enables you to send and receive messages. It is not visible, has no z-order, cannot be enumerated, and does not receive broadcast messages. The window simply dispatches messages.

Creating a message-only window is not much different from creating a normal one. It is just that we have to specify fewer parameters in WNDCLASSEX structure:

WNDCLASSEXA wc; wc.cbSize = sizeof wc; wc.lpfnWndProc = WndProc; wc.lpszClassName = "NameOfThisClass";

The rest of structure members are set to 0 and NULL according to their types. Registration of the class is just as usual:

if (!RegisterClassExA(&wc)) { // handle the error here }

And then we simply create the window:

messageOnlyWindow = CreateWindowExA( 0, "NameOfThisClass ", "Whatever you decide to name the window", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandleA(NULL), NULL);

As you may have noticed, we specify HWND_MESSAGE as parent window, thus telling the operating system that this window is Message-Only.

The last step is the window procedure (WndProc) which we have to define in accordance with the type of the window. This means that we have to handle at least two kinds of Windows Messages – WM_APP and WM_COPYDATA.

WM_APP – this constant equals 0x8000 and is the beginning of the range of message values available for application use (meaning we decide what each and every message means and how it should be handled). The maximum value is 0xBFFF (WM_APP + 0x3FFF). Such messages are ideal for passing control directives between two modules running in different processes.

WM_COPYDATA – messages of this kind are used for data transfers between two modules running in different processes. A particular type of parameter is used as lParam of the SendMessage() – pointer to the COPYDATASTRUCT:

typedef struct tagCOPYDATASTRUCT { ULONG_PTR dwData; DWORD cbData; PVOID lpData; } COPYDATASTRUCT, *PCOPYDATASTRUCT;

dwData – the data to be passed to the receiving application.

cbData – the size, in bytes, of the data, pointed to by the lpData.

lpData – pointer to the data to be passed to the receiving application (may be NULL).



It is important to mention that the data sent to the recipient may not contain pointers to areas not accessible from the receiving process.

From a security perspective, designers of protection mechanisms are rarely paying attention to code injection in general and to interprocess communication methods in particular, preferring to concentrate on anti-debugging and pretending that no other problems exist.

