[标题]:简易Windows密码查看器 
[时间]:2009-10-09 
[摘要]:通过全局钩子获取当前鼠标处的窗口控件句柄,然后直接调用GetWindowText()获取密码文本。 
[关键字]:密码、查看、星号、全局钩子、Hook、WM_COPYDATA、DLL、XP样式 
[环境]:Visual Studio 2008、Visual C++ 6.0 
[作者]:天堂露珠 (wintys@gmail.com) 

[正文]:
    此密码查看器原理:通过全局钩子获取当前鼠标处的窗口控件句柄,然后直接调用GetWindowText()获取密码文本。工程在VC++6.0和VS2008中编译通过。
1、查看密码的DLL工程
    因为查看密码功能要用到全局鼠标Hook,所以要把功能放到一个DLL中。
PasswordViewerMouseHookDLL.h:
#pragma once
#ifdef PSWMOUSEHOOKDLL_API_EXPORTS 
#define PSWMOUSEHOOKDLL_API __declspec(dllexport) 
#else 
#define PSWMOUSEHOOKDLL_API __declspec(dllimport) 
#endif
/* 
Winty:2009-09-29 
调用SetHook()设置Hook,但无需卸载Hook,DLLMain中有清理。 
*/ 
//My Declaration================================ 
#define MAXCOUNT 200 //密码最大长度 
#define DWDATA_PSW_NOTIFY 1 //COPYDATASTRUCT的dwData自定义值
//密码信息结构体,用于发送 
typedef struct tagPswNotify 

    char psw[MAXCOUNT];//password/text 
    POINT pt;//鼠标位置 
    HWND hWnd;//控件句柄 
} PSWNOTIFY , *PPSWNOTIFY;
/*extern表示这里只是变量声明,变量定义在cpp文件中*/ 
extern PSWMOUSEHOOKDLL_API BOOL g_bReadySend;//WM_COPYDATA互斥的标志,因为WM_COPYDATA不能重叠 
extern PSWMOUSEHOOKDLL_API HWND g_hWnd; //接收消息的窗体句柄 
extern PSWMOUSEHOOKDLL_API HHOOK g_hhk; //钩子句柄 
extern PSWMOUSEHOOKDLL_API BOOL g_bView;//是否需要查看密码
//鼠标钩子过程 
PSWMOUSEHOOKDLL_API LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam ); 
//设置钩子(可在窗口的OnInitDialog()中调用) 
PSWMOUSEHOOKDLL_API void SetHook(HWND hWnd);
PasswordViewerMouseHookDLL.cpp:
#include "stdafx.h"
#define PSWMOUSEHOOKDLL_API_EXPORTS 
#include "PasswordViewerMouseHookDLL.h"
#include <stdio.h>
//进程间共享数据,还要在链接器选项里添加:"/SECTION:.MyShare,RWS" 
//或: #pragma   comment(linker,"/SECTION:.MyShare,RWS")  
//查看结果:dumpbin /headers *.DLL 
#pragma data_seg(".MyShare") 
HHOOK g_hhk = NULL;/*Hook句柄*/ 
HWND  g_hWnd = NULL;/*接收消息的窗口句柄*/ 
BOOL  g_bReadySend = TRUE;/*用于同步COPYDATA消息*/ 
#pragma data_seg() 
#pragma   comment(linker,"/SECTION:.MyShare,RWS")
BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved 
                     ) 

    switch (ul_reason_for_call) 
    { 
        case DLL_PROCESS_ATTACH: 
            break; 
        case DLL_PROCESS_DETACH: 
            if(g_hhk != NULL) 
            { 
                UnhookWindowsHookEx(g_hhk); 
                g_hhk = NULL; 
                g_hWnd = NULL; 
            } 
            break; 
        case DLL_THREAD_ATTACH: 
        case DLL_THREAD_DETACH: 
            break;
    } 
    return TRUE; 
}
LRESULT CALLBACK MouseProc( 
                           int nCode,      // hook code 
                           WPARAM wParam,  // message identifier 
                           LPARAM lParam   // mouse coordinates 


    if(nCode == HC_ACTION) 
    { 
        PMOUSEHOOKSTRUCT pMouseHookStruct 
            = reinterpret_cast<PMOUSEHOOKSTRUCT>(lParam); 
        LONG x = pMouseHookStruct-&gt;pt.x; 
        LONG y = pMouseHookStruct-&gt;pt.y; 
        HWND hWnd = pMouseHookStruct-&gt;hwnd;
        HWND hWndFromPoint =  ::WindowFromPoint(pMouseHookStruct-&gt;pt);
        if(hWndFromPoint != g_hWnd  &&  g_bReadySend)//不能获取显示密码的文本框的内容 
        { 
            g_bReadySend = FALSE; 
            char psw[MAXCOUNT]; 
            ::GetWindowText(hWndFromPoint , psw , MAXCOUNT); 
            PSWNOTIFY pswNotify; 
            strcpy(pswNotify.psw , psw); 
            pswNotify.pt.x = x; 
            pswNotify.pt.y = y; 
            pswNotify.hWnd = hWndFromPoint;
            COPYDATASTRUCT cd; 
            cd.lpData = &pswNotify; 
            cd.cbData = sizeof(PSWNOTIFY); 
            cd.dwData = DWDATA_PSW_NOTIFY; 
            ::SendMessage(g_hWnd,  WM_COPYDATA, NULL , (LPARAM)(LPVOID)&cd); 
        } 
    }
    return CallNextHookEx(g_hhk , nCode , wParam ,lParam); 
}
void SetHook(HWND hWnd) 

    g_hWnd = hWnd; 
    g_hhk = SetWindowsHookEx(WH_MOUSE , 
                             MouseProc , 
                              GetModuleHandle("PasswordViewerMouseHookDLL") , 
                             NULL); 
}
     DLL要想将密码消息发送到主窗口显示,要用WM_COPYDATA消息(或其它进程间通信方式),否则会发送失败。如果用WM_SETTEXT,因为全局Hook发送的消息所带的字符串指针lParam可能不能被主窗口访问,从而造成访问异常。
    g_bReadySend的设置是因为WM_COPYDATA消息不能连续发送,必须等前一条消息取走之后才能发送下一条消息。
2、显示密码的工程PasswordViewer
    创建一个对话框工程PasswordViewer。
使用DLL步骤:
  • 把PasswordViewerMouseHookDLL工程生成的PasswordViewerMouseHookDLL.dll、PasswordViewerMouseHookDLL.lib,以及PasswordViewerMouseHookDLL.h复制到PasswordViewer工程。 
    [参考资料]: 
    [附件]:
  • 在PasswordViewerDlg.cpp 中加入#include "PasswordViewerMouseHookDLL.h"和#pragma comment(lib , "PasswordViewerMouseHookDLL.lib")
    如果要设置最后生成XP样式的窗口,得在PasswordViewerDlg.cpp 中加入
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
    PasswordViewerDlg.cpp主要代码为响应WM_COPYDATA消息,把消息内容显示到窗口上:
BOOL CPasswordViewerDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct) 

    if(pCopyDataStruct-&gt;dwData == DWDATA_PSW_NOTIFY && !g_bReadySend) 
    { 
        PPSWNOTIFY pPswNotify = (PPSWNOTIFY)pCopyDataStruct-&gt;lpData;
        CString strMousePosition; 
        strMousePosition.Format("(%3d,%3d)" , pPswNotify-&gt;pt.x ,pPswNotify-&gt;pt.y); 
        m_strMousePosition = strMousePosition; 
        CString strPsw; 
        strPsw.Format("%s" , pPswNotify-&gt;psw); 
        m_strPsw = strPsw; 
        CString strHWnd; 
        strHWnd.Format("%p" , pPswNotify-&gt;hWnd); 
        m_strHWnd = strHWnd; 
        UpdateData(FALSE);
        g_bReadySend = TRUE;
        return TRUE; 
    } 
    else 
    { 
        CString str(_T("未发现窗口")); 
        m_strPsw = str;
        UpdateData(FALSE);
        return CDialog::OnCopyData(pWnd, pCopyDataStruct); 
    }    
}
    别忘了要在CPasswordViewerDlg::OnInitDialog()中调用DLL的SetHook(m_hWnd),初始化全局钩子。
3、运行
    运行结果如图:
【cpp_PasswordViewer.jpg】
[附件]:
PasswordViewer.zip:
PasswordViewer.exe、PasswordViewerMouseHookDLL.dll、工程源代码