环境:VC6/VC7, MS Platform Core SDK, IE4.0+, WinNT/2K/XP (在英文/中文/日文版的Win2k/XP 及IE6.0+SP1上测试通过)
关键字:Windows 钩子,IE COM 对象,Win2k 安全上下文 IE编程 工具 系统
摘要
本文将介绍一个工具,它不仅能偷窥各种桌面程序的密码框,还能窥到IE页面中的密码框,这个程序就是本文要介绍的——SuperPasswordSpy++。
使用 Windows 钩子偷窥远程进程(或者说桌面程序)密码框内容不是太难,但要偷窥到网页上密码输入域的内容要如何做呢?显然,在网页里的密码输入框不是一个窗口,你得借助 IHTMLDocument2 接口来枚举并吸取密码。本文提供的工具程序将向你展示破解密码编辑公共控件和IE密码输入域的内容。下图是程序运行的截图:
图一:SuperPasswordSpy++ 偷窥Hotmail 页面上的密码输入框
图二 SuperPasswordSpy++ 偷窥IIS5.0和WinXP密码编辑框内容
程序架构
在你围绕屏幕拖动放大镜,程序便捕获鼠标位置并跟踪鼠标的移动,只要鼠标移到某个新窗口,程序便检查该窗口的类名以及窗口式样,确定窗口是否为密码编辑框或IE(IE实际上是一个浏览器控件,为方便起见叫IE)。如果是IE,那么钩子DLL必须被立即注入到IE中,以确定IE是否包含密码输入域。
我们有两种可选的方法来实现钩子:
图三Spy 程序钩子的一般架构
上图涉及到五个步骤:
因为Spy程序发送用户消息,第二步不会“阻塞”,Spy程序不知道第四步何时发生。通常这种Spy程序使用WM_COPYDATA来传输字节数据,即“发送”消息。此处不足之处是当用户来回移动放大镜时可能会有这样的情况——Spy程序发现密码编辑框,注入DLL到目标程序,发送用户消息,这时用户突然移动放大镜到另一个目标程序的密码输入框,此时Spy程序不得不从老的目标程序中卸载DLL钩子,钩子进入新的目标程序并发送消息。不幸的是,来自老的目标程序的WM_COPYDATA 消息已经进入目标程序的消息队列,如果你不将目标窗口句柄信息添加到WM_COPYDATA,,你便无法告知当前目标程序的数据。
但是,第二种方法也有其弊端。考虑下面的情况,假设相同的窗口中有两个密码编辑框,用户同时只能顾一个密码框,第一种方法,我们可以检查两个密码框属于同一个线程,钩子DLL只运作一次。而第二种方法,钩子要运作两次,这造成一定的开销(本文本程序可以忽略这种开销)。
实现细节描述
1、如何从浏览器控件窗口句柄获取IHTMLDocument(参见MSDN KB Q249232——HOWTO: 如何从从HWND获取IHTMLDocument2)
BOOL HWnd2HtmlDocument() { CoUninitialize(); HINSTANCE hInst = ::LoadLibrary( _T("OLEACC.DLL") ); if ( hInst == NULL ) return FALSE; LRESULT lRes; UINT nMsg = ::RegisterWindowMessage( _T("WM_HTML_GETOBJECT") ); ::SendMessageTimeout( g_hTarget, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes ); LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress( hInst, "ObjectFromLresult"); if ( pfObjectFromLresult == NULL ) { ::FreeLibrary( hInst ); CoUninitialize(); return FALSE; } WCHAR strDoc[] = L"{626fc520-a41e-11cf-a731-00a0c9082637}"; //IID_IHTMLDocument2 CLSID CLSID uuidDoc; HRESULT hrDoc = CLSIDFromString((LPOLESTR)strDoc, &uuidDoc //IID_IHTMLDocument2 ); if(!SUCCEEDED(hrDoc)) { ::FreeLibrary( hInst ); CoUninitialize(); return FALSE; } HRESULT hr = (*pfObjectFromLresult)( lRes, uuidDoc, //IID_IHTMLDocument, 0, (void**)&g_lpHTMLDocument2); if ( SUCCEEDED(hr) ) { //OK, We Get Here Successfully } else { ::FreeLibrary( hInst ); CoUninitialize(); return FALSE; } ::FreeLibrary( hInst ); CoUninitialize(); return TRUE; }在此我需要解释 g_hTarget 是浏览器句柄,其类名是“Internet Explorer_Server”。通常,如果使用MS IE浏览器,不会出现问题,但某些应用使用 Web 浏览器的ActiveX 控件,在浏览器导航之前,“Internet Explorer_Server”窗口是不存在的。如图我们来看一个例子:
图四 Spy++截图:一个对话框有两个MS Web Browser ActiveXs
图四是运行Spy++的一张截图,该对话框(00060294)有两个浏览器,只有浏览器000202D6导航到某些URL。你可以做个实验,在对话框中放一个WebBrowser ActiveX,用 Spy++看看结果。
2、当前页面是否包含密码输入域
DWORD CheckHtmlDocument() //返回: 0 —— 无密码输入,否则 —— 有密码输入 { MSHTML::IHTMLElementCollection *pForm; HRESULT hr = g_lpHTMLDocument2->get_all(&pForm); //g_lpHTMLDocument2 是一个IHTMLDocument2 指针 if(FAILED(hr)) return 0; long len; pForm->get_length(&len); //How many elements on this form? DWORD dwRet = 0; for(int i = 0; i < len; i++) { LPDISPATCH lpItem = pForm->item(CComVariant(i), CComVariant(i)); MSHTML::IHTMLInputElementPtr lpInput; HRESULT hr = lpItem->QueryInterface(&lpInput); //它是输入域吗? if(FAILED(hr)) continue; _bstr_t type(_T("password")); if(lpInput->Gettype() == type) //Check Field Type { //_bstr_t x = lpInput->Getvalue(); //If you want its string dwRet++; } lpItem->Release(); //记住释放! lpItem = NULL; } pForm->Release(); pForm = NULL; return dwRet; }3、从当前页面密码输入域吸取密码
_bstr_t x = lpInput->Getvalue(); //And you go! LPCTSTR lpWhatEver = (LPCTSTR)x; //在这里对密码进行处理SuperPasswordSpy++ 程序说明——该程序基于Unicode,需要在WinNT/2K/XP + IE4.0+中运行。
读者可以通过本文附带的程序代码了解如何跟踪窗口和远程进程钩子。同时极力推荐读者研究 MS Platform SDK 的例子程序 SPY 和 Brian Friesen 先生的 PasswordSpy。笔者不喜欢“重新发明轮子”之类的事情,本文涉及的代码借鉴了 SPY 例子(如鼠标跟踪)以及PasswordSpy(如函数 SmallestWindowFromPoint和资源)的实现。如果读者有关于钩子技术的疑问,请阅读Brian Friesen 的相关文章,他的文章在这方面讲很详细。此外有关DLL中共享区的详细实现方法建议阅读 Jeffrey Richter 的“Programming Application for MS Windows”第四版。
下面是本文程序SuperPasswordSpy++ 实现中的其它一些细节:
1、判断密码编辑框的条件
BOOL IsPasswordEdit(HWND hWnd) { TCHAR szClassName[64]; int nRet = GetClassName(hWnd, szClassName, 64); if(nRet == 0) return FALSE; szClassName[nRet] = 0; if(::lstrcmp(szClassName, _T("Edit")) != 0 && ::lstrcmp(szClassName, _T("TEdit")) != 0 &&| ::lstrcmp(szClassName, _T("ThunderTextBox")) != 0 ) return FALSE; //Here, is it OK? DWORD dw = ::GetWindowLong(hWnd,GWL_STYLE); dw &= ES_PASSWORD; if(dw == ES_PASSWORD) return TRUE; return FALSE; }以上代码是SuperPasswordSpy++ 实现,要注意一点:密码编辑框的判断是根据类名实现的,笔者在代码中的判断方法是检查类名是否为“TEDIT," "IRIS.PASSWORD” 或者“"EDIT.”。这是Borland的命名规范:TxxxClass。“ThunderTextBox”是由Visual Basic创建得类名。我无法保证今后Windows系统中的应用程序密码编辑框的类名不会改变。谁知道Visual Studio今后的版本中密码编辑框会叫什么。如果不幸碰到这样的情况,请大家自行修改SuperPasswordSpy++的相关代码。
图五
图五 SuperPasswordSpy++ 窥视Win2K 服务器的“更改密码”的编辑框,用“Ctrl+Alt-Del”就可以看到“更改密码”按钮。PasswordSpy++在Win2K的登陆桌面的SYSTEM上下文中已经启动,这时可以读取到“更改密码”框中的秘密。在Windows2003系统中这个方法就不灵了。
为了完成这个实验,你得借助工具在Windows的登陆桌面(Winlogon Desktop)来启动程序。你可以到 codeguru 获取“GUI-RunAs”程序。选择Winlogon桌面,不要输入用户名,这样就可以使用“SYSTEM”身份,记住你必须具有 Administrator 权限来完成这个工作。按照文章中的说明,你可能需要注销当前会话一次(只要一次)以便启用某些还不具备的权限。有些读者来信说Windows系统已经有一个“RunAs”命令行程序了。我当然知道,但是微软品牌的这个“RunAs”命令行工具无法选择“桌面”来启动程序,而我的工具能做到。如果你在使用这个工具时有什么问题(例如,启动的程序GUI失败),请先用“SYSTEM”身份启动这个工具,然后再启动你使用的程序。
最后,为了获得屏幕截图,出现WinLogon桌面时按“Print Screen”,回到默认桌面,将它粘贴到MSPaint程序。你还可以用 RunAs工具在WinLogon屏幕启动MSPaint,不用来回切换屏幕。你还会发现这个 RunAs工具另一个好处,以“SYSTEM”身份启动任务管理器,杀死一些顽固的进程(包括Windows服务)。
2、编辑框的偷窥与反偷窥
有人如何反偷窥。其实不需要花费太大的功夫就可以实现反偷窥。下面的代码例子使用MFC,首先派生一个 CEdit,我最初想改写PreTranslate函数,就像下面这样:
BOOL CAntiPeekEdit::PreTranslateMessage(MSG* pMsg) //这个方法不灵! { if(pMsg->message == WM_GETTEXT) { //Only Report Text When Passing a Fixed Length Buffer; if(pMsg->wParam == 1024) //The Number Only You Know { } else { ::lstrcpy((LPTSTR)(pMsg->lParam), _T("Nothing")); return TRUE; } } return CEdit::PreTranslateMessage(pMsg); }可惜 if 语句中的代码不会被调用,为什么呢?只有MFC 开发团队知道。没办法我把注意力转到函数 WindowsProc,这样就可以行得通了。
LRESULT CAntiPeekEdit::WindowProc( UINT message, WPARAM wParam, LPARAM lParam) //This works! { if(message == WM_GETTEXT) { if(wParam == 1024) //The Number Only You Know { return CEdit::WindowProc(message, wParam, lParam); } else { ::lstrcpy((LPTSTR)(lParam), _T("Nothing")); //Insert Dummy Text Here To the Peeker return 7; } } return CEdit::WindowProc(message, wParam, lParam); }在你自己的程序中,如果想要获得密码框的文本,你得像下面这样做:
TCHAR sz[1024]; ::SendMessage(hPasswordEdit, WM_GETTEXT, 1024, (LPARAM)sz);如果其它例程调用以获取秘密,由WM_GETTEXTLENGTH 会得到正确的长度,但是当正确的长度缓冲给了我们的 AntiPeekEdit,我们便知道它由某些其它的非安全源代码调用,所以我们可以返回垃圾数据。你还可以完全放弃 WM_GETTEXT 并使用 WM_USER + 123 消息来获取文本。 让我们回头来考虑如何化解这种AntiPeekEdit。我们知道为何要使用钩子DLL,同时在远程进程中查询密码,Win2K密码编辑框是不接受来自本进程边界以外的 WM_GETTEXT 消息的。以上策略以用户定义的过程代替标准的Edit类窗口过程,那么,反过来当 SuperPasswordSpy++ 进行偷窥活动时,如何用标准的Edit类窗口过程代替用户定义的过程呢。
HWND hParent = ::GetParent(g_hTarget); //g_hTarget 是我们要的秘密编辑框句柄 HWND hwndEdit = CreateWindow( _T("EDIT"), // 预定义类 NULL, // 没有窗口标题 WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, 0, 0, 0, 0, // 在WM_SIZE 消息中设置大小 hParent, // 父窗口 (HMENU)123, // 编辑框ID——注意在同胞窗口中必须唯一 (HINSTANCE) GetWindowLong(g_hTarget, GWL_HINSTANCE), NULL); // 不需要指针 //获得标准的Edit 类窗口过程 LONG_PTR lpNewEdit = GetWindowLongPtr(hwndEdit, GWLP_WNDPROC); LONG_PTR lp = ::SetWindowLongPtr(g_hTarget, GWLP_WNDPROC, (LONG_PTR)lpNewEdit); //在这里取密码——只在这里调用 //SuperPasswordSpy++ SendMessage(g_hTarget, WM_GETTEXT, sizeof(szBuffer) / sizeof(TCHAR), (LPARAM)szBuffer); //重置原来的窗口过程 ::SetWindowLongPtr(g_hTarget, GWLP_WNDPROC, (LONG_PTR)lp);请注意控件ID参数何时创建伪编辑框,在同胞中它必须唯一,我此处使用123作为占位符。你可以编写额外代码来枚举同胞窗口并获得唯一ID,并记住最后销毁伪编辑框。 在大多数情况下,这样做实在是有牛刀杀鸡之嫌,所以我在SuperPasswordSpy++中没有这种方法,以便保证性能和高稳定性。然而,一旦你真的遇到这样的反偷窥秘密编辑框,去掉SuperPasswordSpy++中注释的代码即可,并牢记伪编辑框的控制ID一定要惟一。 如果有人热衷于反-反-反偷窥,那么也许你可以添加一个全局变量标志,在取密码前设置该标志,取完之后再置回来…那么为何不用某种算法在WM_GETTEXT消息处理例程中加密文本呢?
3、其它的偷窥Spy工具
如果你想偷窥 MSN Messenger/Windows Messenger 的聊天信息,请参考笔者的另外一篇文章“MessengerSpy++”。如下图所示:
图五WinXP上用MessengerSpy++偷窥MSN Messenger 聊天信息
我们可以获得100%的 RTF文本以及MSN Messenger的表情图像(右边窗口)。注意支持操作系统是 Win2k/XP,支持 MSN Messenger 4.6, 4.7 和5.0.。此外这个程序还可以发送文本和ICON图标到MSN Messenger 并让Messenger 将它发送给另一个人。
3、WinXP “修改用户密码”控制面板小程序
对于这个控制面板小程序,即便获取了“Internet Explorer_Server” 窗口类名,由于密码输入域含在一个ActiveX中,在我的英文 WinXP专业版系统中,其CLSID为A5064426-D541-11D4-9523-00B0D022CA64(res://D:\WINDOWS\system32\nusrmgr.cpl/nusrmgr.hta)。所以从ActiveX 中读取密码几乎完全不可能。
欢迎访问最专业的网吧论坛,无盘论坛,网吧经营,网咖管理,网吧专业论坛
https://bbs.txwb.com
关注天下网吧微信/下载天下网吧APP/天下网吧小程序,一起来超精彩
|
本文来源:vczx 作者:佚名