Microsoft & .NET Visual C# Peeking into Password Edit Controls & Internet Explorer - Super Password Spy++

Peeking into Password Edit Controls & Internet Explorer – Super Password Spy++

Environment: Visual C++ 6 or 7, MS Platform Core SDK, IE4.0+,

WinNT/2K/XP ONLY (Test has passed on English/Chinese/Japanese

Win2k/XP with IE6.0+SP1)

Key Technology Used: Windows Hook, IE COM Object, Win2k Security

Context

Introduction

If you need a tool to peek not only the password edit boxes on

different programs, but also the **** password input field on a homepage

residing inside Internet Explorer, here it is—SuperPasswordSpy++.

With Windows Hook, it is not difficult to peek into a remote process’s

password edit, but how about the password input field inside a homepage,

say, in an Internet Explorer window? The password input field, obviously,

is NOT a window; you have to turn to the IHTMLDocument2 interface to

enumerate the inside stuff and extract the password. This program will

show you how to do with both the password edit common control and the

password input field inside IE. Following is the screen shot of the

program running, peeking the password in both cases.

Figure 1: SuperPasswordSpy++ Peeking a Hotmail “Forget Your

Password” Page

Figure 2: SuperPasswordSpy++ Peeking a Password Edit inside IIS 5.0

on WinXP

Architecture

After the user begins to drag the magnifying glass around on the

screen, the program captures the mouse and keeps track of the mouse move

message. Whenever the mouse moves onto a new window, it checks the windows

class name and window style to decide whether it is a password edit or an

IE. (Note: actually, it is a Web browser control; I use IE for short.) In

the latter case, a hook DLL has to be injected into the IE immediately to

decide whether the IE contains a password input field.

Please Note: Here we have two options to implement the hook:

The first way is setting a WH_GETMESSAGE hook, which was done by

Mr. Brian Friesen in his article PasswordSpy in the

Samples Section of www.codeguru.com.

This hook DLL will intercept the posted message and a synchronization

object (mutex, event, and so on) will be applied inside it. Please refer

to Figure 3:

Figure 3: General Spy Program’s Hook Architecture

There are 5 steps involved:

  1. The Spy program injects a hook DLL into the Target program.
  2. The Spy program posts a user message to the Target program that will

    be intercepted by the DLL.

  3. The DLL intercepts the user message and reads the password edit

    content.

  4. The DLL sends the data back to the Spy program.
  5. The Spy program gets the data and un-hooks the DLL.

Because the Spy program posts the user message, Step 2 will not “block”

and the Spy program does NOT know when Step 4 will happen. Usually, this

kind of Spy program uses WM_COPYDATA to transfer byte data; it is a “Send”

message. Okay, here is the tough thing: The user may move the magnify

glass here and there, and let’s image the following case. The Spy program

found a password edit, injected the DLL into the target program, posted

the user message, and suddenly the user moved the magnify glass to another

password edit, so the Spy program has to un-hook the DLL out of the old

target program, hook into the new target program, and post the user

message. Unfortunately, the WM_COPYDATA from the old target program has

queued into the Spy program’s message queue, and if you do not append the

target window handle information to the WM_COPYDATA, you cannot tell the

data of the current Target program from the old target program…

Another option is setting the WH_CALLWNDPROC hook. It’s all the

same as the first option, except in Step 2 we send the message instead of

posting it to the target program. The hook DLL will intercept the user

message and make inside password reading, and call SendMessage with

WM_COPYDATA to pass the data to the Spy program. In one word, Step 2 is

blocked until Steps 3 and 4 are finished; so, after Step 2 is finished, we

can do Step 5 directly. In this way, the code will be much simpler than

the first way. It is just like Block Socket and Non-Block Socket

programming.

Sure, the second way has its disadvantage. Consider the following case:

There are two password edits on the same window and the user must leave

one to enter another. In the first way, we can check the two edits are on

the same thread, so we can hook the DLL only once. In the second way, we

hook twice which means (ignorable here in this program) a performance

penalty.

Implementation Description

1. How to get IHTMLDocument from Browser Control Window

Handle:

(with ref. from MSDN KB Q249232 HOWTO: Get IHTMLDocument2 from a

HWND)

BOOL HWnd2HtmlDocument()
{
  HINSTANCE hInst = ::LoadLibrary( _T("OLEACC.DLL") );
  if ( hInst == NULL ) return FALSE;
  LRESULT lRes;
  UINT nMsg = ::RegisterWindowMessage( _T("WM_HTML_GETOBJECT") );
  ::SendMessageTimeout( g_hTarget, nMsg,
    0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes );
  LPFNOBJECTFROMLRESULT pfObjectFromLresult = 
        (LPFNOBJECTFROMLRESULT)::GetProcAddress( hInst,
        "ObjectFromLresult");
    if ( pfObjectFromLresult == NULL )
    {
      ::FreeLibrary( hInst );
      CoUninitialize();
      return FALSE;
    }
    WCHAR strDoc[] = L"{626fc520-a41e-11cf-a731-00a0c9082637}";
                   //IID_IHTMLDocument2 

CLSID
    CLSID uuidDoc;
    HRESULT hrDoc = CLSIDFromString((LPOLESTR)strDoc,
      &uuidDoc //IID_IHTMLDocument2
      );
    if(!SUCCEEDED(hrDoc))
    {
      ::FreeLibrary( hInst );
      CoUninitialize();
      return FALSE;
    }
      HRESULT hr  = (*pfObjectFromLresult)( lRes, uuidDoc,
      //IID_IHTMLDocument,
      0, (void**)&g_lpHTMLDocument2);
      if ( SUCCEEDED(hr) )
      {
        //OK, We Get Here Successfully
      }
      else
      {
        ::FreeLibrary( hInst );
        CoUninitialize();
        return FALSE;
      }
      ::FreeLibrary( hInst );
      CoUninitialize();
      return TRUE;
     }

There is something I need to explain here: g_hTarget is the handle of

the browser, and its class name is “Internet Explorer_Server.” Usually, it

is not a problem with MS Internet Explorer, which always starts to

navigate somewhere when it starts. But, in some applications hosting a Web

Browser with ActiveX, before the browser navigates, the “Internet

Explorer_Server” window does NOT

exist. Let me give you an example:

Figure 4: Spy++’s Screen Shot on a Dialog Hosting Two MS Web

Browser ActiveXs

It is a screen shot from Spy++. There are two browsers on this dialog

(00060294); only browser 000202D6 navigates to some URL. You can make an

experiment by putting a WebBrowser ActiveX on a dialog, and check this by

Spy++.

2. Whether Current Homepage contains a Password Input Field:

DWORD CheckHtmlDocument()
//Ret: 0 -- No Password Input; Else -- Password 

Input Number
      {
MSHTML::IHTMLElementCollection *pForm;
HRESULT hr = g_lpHTMLDocument2->get_all(&pForm);
//g_lpHTMLDocument2 is a pointer of 

IHTMLDocument2
if(FAILED(hr)) return 0;
long len;
pForm->get_length(&len); //How many 

elements on this form?
DWORD dwRet = 0;

for(int i = 0; i < len; i++)
             {
LPDISPATCH lpItem = pForm->item(CComVariant(i), CComVariant(i));
MSHTML::IHTMLInputElementPtr lpInput;
HRESULT hr = lpItem->QueryInterface(&lpInput); //Is it a input field?

if(FAILED(hr)) continue;
_bstr_t type(_T("password"));
                   
if(lpInput->Gettype() == type)      //Check 

Field Type
{
//_bstr_t x = lpInput->Getvalue();  //If you 

want its string
dwRet++;
}
lpItem->Release();  //Remember To Release 

this!
lpItem = NULL;
              }
pForm->Release();
pForm = NULL;
return dwRet;
       }

3. Extract password from Current Homepage’s Password Input

Field:

_bstr_t x = lpInput->Getvalue();    //And you 

go!

LPCTSTR lpWhatEver = (LPCTSTR)x;

     //Do something with the password here

Special Note with SuperPasswordSpy++

Once again, SuperPasswordSpy++ is for

WinNT/2K/XP only and IE 4.0+ is needed; it is a Unicode-based

Program.

Interested readers can learn how to track window and hook remote

processes by studying the source code of SuperPasswordSpy++. It is highly recommended

the readers study MS Platform SDK’s Spy sample and PasswordSpy of Mr.

Brian Friesen. The author dislikes “recreating the wheel” things, and

borrows mouse tracking code from Spy sample and a function

“SmallestWindowFromPoint,” plus the resource file from PasswordSpy. And,

if the reader has questions about the hook technique, please read Mr.

Brian Friesen’s article; it is already detailed enough. In addition,

readers can read Chapters 19 and 20 of Programming Application for MS

Windows, 4th Edition written by Jeffrey Richter, published by

Microsoft Press, 1999, for more detailed information about Shared Sections

in DLLs.

Well, besides, something unclear with SuperPasswordSpy++s implementation does

exist:

1. Criteria of Judging Password Edit

BOOL IsPasswordEdit(HWND hWnd)
{
TCHAR szClassName[64];
int nRet = GetClassName(hWnd, szClassName, 64);
if(nRet == 0) return FALSE;
szClassName[nRet] = 0;
if(::lstrcmp(szClassName, _T("Edit")) != 0) return FALSE;
    //Here, is it OK?

DWORD dw = ::GetWindowLong(hWnd,GWL_STYLE);
dw &= ES_PASSWORD;
if(dw == ES_PASSWORD)
return TRUE;
return FALSE;
}

Above is the implementation of SuperPasswordSpy++. But, in a tool sample

called Eureka (residing in

http://www.codeguru.com/samples/Eureka.shtml, unfortunately this tool

is for Win9X/Me), the author judges a password edit if its class name is

“TEDIT,” “IRIS.PASSWORD,” or “EDIT.” I played with Delphi and Borland C++

six years ago, and I know it is Borland’s name convention TxxxClass. But I

have no Delphi at hand, so if anyone can help check this, please comment

below. Thanks in advance. And I will add multiple name matching if

necessary.

2. When Multiple Frame Sets Exist Inside an Html Document…

The current (Dec. 2002) version of SuperPasswordSpy++ assumes there is only one

frame inside an HTML document if it contains a password field. It is fine

with most passworded homepages (for example, MSN Hotmail). But, to make it

perfect, I will add multiple frame set support in next version of SuperPasswordSpy++. Please check this in my

site later.

More Words about *****

1. Windows Logon Password: (Win2K ONLY)

It sounds a little crazy, but please look at the following figure:

Figure 5: SuperPasswordSpy++’s Peeking “Change Your Password” on

WinLogon Screen of Win2K Server

I have only the Japanese Win2K server at hand, so there is some

Japanese here, but I think you can see it is from “Ctrl+Alt-Del,” then

press the “Change Password” button. PasswordSpy++ has been launched under SYSTEM

context on Winlogon Desktop of this Win2K, and it works. I mean, it

can read the password from the “Change Password” Dialog. Oh, by the way,

you will never know the current password because it is never shown.

To conduct this experiment, you have to use some tool to launch a

program on Winlogon Desktop of Windows. You can go here to get my “GUI

RunAs” program. You have to choose Winlogon desktop, make the user name

edit box blank to use SYSTEM identity, and remember you must have

Administrator rights to do so. Follow the article’s instructions and you

may need to log off the current session once (only once) to enable some

privileges if you do not already have them. Some reader sent me an e-mail

saying there is a “Runas” command line tool in Windows OS already. I know,

but with this command-line tool, you can NOT choose the Desktop to launch

the program. If you have problems with the tool (for example, the launched

program’s GUI is blocked), please launch the tool itself as SYSTEM first,

and then launch the program you use.

The last word: To get the screen shot, press “Print Screen” when you

are on the Winlogon desktop, go to the default desktop, and paste it into

MSPaint. You can also use the RunAs tool to launch MSPaint onto the

Winlogon screen too, and do your job there without switching back and

forth. (You can resize MSPaint, but do not minimize it; otherwise, you

have no way to restore it unless you make a program to send a message to

it.) And, you may find this RunAs GUI tool is a good way to launch a task

manager as SYSTEM and kill some stubborn process (including NT

Service).

2. Anti Peeking Edit and Crack Anti-Peeking Edit ( -* – =

+)

I notice someone asked how to do anti peeking. Well, it does not cost

too much to implement anti-peeking. Following is a code example using MFC.

First, derive an edit class from CEdit. My first idea is to override its

PreTranslate, this way:

BOOL CAntiPeekEdit::PreTranslateMessage(MSG* pMsg)
//This does NOT work!
{
if(pMsg->message == WM_GETTEXT)
{
//Only Report Text When Passing a Fixed Length 

Buffer;
if(pMsg->wParam == 1024)  //The Number Only You 

Know
{
}
else
{
::lstrcpy((LPTSTR)(pMsg->lParam), _T("Nothing"));
return TRUE;
}
}
return CEdit::PreTranslateMessage(pMsg);
}

Unfortunately, the inside if clause will never be called. Why? MFC team

guys know. Well, I have to turn to the virtual function WindowsProc; this

time it works:

LRESULT CAntiPeekEdit::WindowProc( UINT message, 
                                   WPARAM wParam,
                                   LPARAM lParam)
//This works!
{
if(message == WM_GETTEXT)
{
if(wParam == 1024)  //The Number Only You 

Know
{
return CEdit::WindowProc(message, wParam, lParam);
}
else
{
::lstrcpy((LPTSTR)(lParam), _T("Nothing"));
//Insert Dummy Text Here To the Peeker
return 7;
}
}
return CEdit::WindowProc(message, wParam, lParam);
}

In your own program, when you want to get the password text, you must

do the following:

TCHAR sz[1024];
::SendMessage(hPasswordEdit, WM_GETTEXT, 1024, (LPARAM)sz);

If other routines call to retrieve the password, WM_GETTEXTLENGTH will

tell them the correct length, but when the correct length buffer is to our

AntiPeekEdit, we know it is called from some other un-secure source, so we

can just send back the junk. Well, you can also abandon WM_GETTEXT

completely and use a WM_USER + 123 message to get the text out.

Let’s go back to how to counterstrike this kind of AntiPeekEdit. Well,

we know the reason we use hook DLL and query password from inside the

remote process is the Win2K password-style edit will not accept WM_GETTEXT

from outside the process boundary. And, the above strategy replaces the

standard Edit class Windows Procedure with a user-defined one. So, how

about replacing this user-defined procedure back to the standard Edit

class Windows Procedure when our lovely SuperPasswordSpy++ peeks it:

HWND hParent = ::GetParent(g_hTarget);
//g_hTarget is the password edit handle we are 

interested
HWND hwndEdit = CreateWindow(
_T("EDIT"),  // predefined class
NULL,  // no window title
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
0, 0, 0, 0,  // set size in WM_SIZE message
hParent,     // parent window
(HMENU)123,
// edit control ID -- Note: Must be Unique Among 

Sibling Windows
(HINSTANCE) GetWindowLong(g_hTarget, GWL_HINSTANCE),
NULL);    // pointer not needed
//Get the standard Windows Procedure of Edit 

class
LONG_PTR lpNewEdit = GetWindowLongPtr(hwndEdit, GWLP_WNDPROC);
LONG_PTR lp = ::SetWindowLongPtr(g_hTarget, GWLP_WNDPROC,
                                (LONG_PTR)lpNewEdit);

//Password Fetch Here --- I only use this call 

in
//SuperPasswordSpy++
SendMessage(g_hTarget, WM_GETTEXT, sizeof(szBuffer) /
            sizeof(TCHAR), (LPARAM)szBuffer);


//Reset the original Windows Procedure
::SetWindowLongPtr(g_hTarget, GWLP_WNDPROC, (LONG_PTR)lp);

Please note the Control ID parameter when creating the fake edit; it

must be unique among its siblings. Well, I use 123 as a placeholder here.

You can write addition code to enumerate the sibling windows and get a

unique ID, and remember to destroy the fake edit in the end.

But, it is really an overkill in most cases, so I did not include the

above code in SuperPasswordSpy++ to

keep performance and stability high. But, once you really meet such a

anti-peek password edit, uncomment the additional code in the SuperPasswordSpy++ source code, and keep an eye

on keeping the fake edit control ID unique.

If someone is mad asking if we have way to anti- anti- anti-peeking,

well, maybe you can add a global variable flag, before you fetch password

set this flag, and after reading, reset it…. Then why not use some

algorithm to encode the text in WM_GETTEXT handler? faint...

3. More Peeking Spy Tools…

>If you need to peek into MSN Messenger/Windows Messenger inside

information and chat contents, please refer to my previous article

MessengerSpy++ (residing in http://www.codeguru.c

om/misc/MessagerSpy.html). The following figure shows we can get the

100% fresh RTF text and these Emotional Icons from MSN Messenger (the

right-side window). Note: it is for Win2k/XP and support MSN Messenger

4.6, 4.7, and 5.0. And what’s more, it can send text/icons to MSN

Messenger and let Messenger send it to the other person.

Figure 5: Mate Tool MessengerSpy++’s Peeking a MSN Messenger Chat

on WinXP

3. WinXP “Change User Password” Control Panel Applet

Unfortunately, even it is inside “Internet Explorer_Server”

(res://D:WINDOWSsystem32nusrmgr.cpl/nusrmgr.hta), the password input

field is inside an ActiveX with CLSID:A5064426-D541-11D4-9523-00B0D022CA64

in my English WinXP Professional. So, it is completely impossible to read

the password from the ActiveX black box.

Acknowledgements

I would like to express my thanks to the following persons and

institute: Prof. Moriya Shinji, Human Interaction Lab of Tokyo Denki Univ., Media Center of

Tokyo Denki Univ., for their academic direction, warm-hearted help,

and continuous encouragements in my career life.

Also thanks to FujiSoftware ABC Ltd. for the senior developer

position offered, plus their wage.

Thanks to the following article/code contributor on CodeGuru: Mr.

Giancarlo Iovino for his HyperLink Control (I fixed a few lines of code so

it can go under Unicode now), and Mr. Brian Friesen for his thesis-level

article and tool PasswordSpy.

Downloads

# # #

Latest Posts

Related Stories