密码破解器
一个简单的 BHO,用于检索用户 ID 和密码。
背景
您是否使用过任何密码管理器和自动表单填充软件? AI RoboForm 是业内历史最悠久、最好的。最新版本的 Firefox 和 Netscape 也通过 Passcard 支持这一点。想象一下开发一个可以检索用户 ID 和密码的软件!为了实现上述目标,我需要开发一个 Internet Explorer 插件或 BHO。关于如何使用 ATL 开发 BHO 的文章有很多,因此我将跳过这一步,专注于如何处理事件和访问 DOM 以检索用户 ID 和密码。脑海中浮现的基本问题是我们如何检测给定的页面是否是登录页面。如果您能找到答案,您就可以完成剩下的工作。经过一些实验,我发现我们应该仅在页面至少有一个类型为 "password" 的输入字段时才尝试检索页面的密码。另一件重要的事情是,大多数登录页面只有一个类型为 <INPUT TYPE=TEXT>
的对象,并且只有一个类型为 <INPUT TYPE=PASWWORD>
的对象。
如何检索用户 ID 和密码
当浏览器完成页面下载后,它会发送一个事件 DISPID_DOCUMENTCOMPLETE
。在这里,我们应该检查页面是否是登录页面。要检测这一点,您必须搜索文档对象中的所有元素,并找出是否存在任何类型为 "password" 的元素。如果找到一个,我们几乎可以肯定这是一个登录页面。
连接到 <FORM> 事件
//
CComPtr<IDISPATCH> spDisp;
HRESULT hr = m_pWebBrowser2->get_Document(&spDisp);
if (SUCCEEDED(hr) && spDisp)
{
// If this is not an HTML document (e.g., it's a Word doc or a PDF), don't sink.
CComQIPtr<IHTMLDOCUMENT2 &IID_IHTMLDocument2> spHTML(spDisp);
if (spHTML)
{
/*there can be frames in HTML page enumerate each of frameset or iframe
and find out if any of them contain a login page*/
EnumFrames(spHTML);
}
}
void CIeLoginHelper::EnumFrames(CComPtr<IHTMLDocument2>& spDocument)
{
CComPtr<IIHTMLDocument2> spDocument2;
CComPtr<IIOleContainer> pContainer;
// Get the container
HRESULT hr = spDocument->QueryInterface(IID_IOleContainer,
(void**)&pContainer);
CComPtr<IIEnumUnknown>pEnumerator;
// Get an enumerator for the frames
hr = pContainer->EnumObjects(OLECONTF_EMBEDDINGS, &pEnumerator);
IUnknown* pUnk;
ULONG uFetched;
// Enumerate and refresh all the frames
BOOL bFrameFound = FALSE;
for (UINT nIndex = 0;
S_OK == pEnumerator->Next(1, &pUnk, &uFetched);
nIndex++)
{
CComPtr<IIWebBrowser2> pBrowser;
hr = pUnk->QueryInterface(IID_IWebBrowser2,
(void**)&pBrowser);
pUnk->Release();
if (SUCCEEDED(hr))
{
CComPtr<IIDispatch> spDisp;
pBrowser->get_Document(&spDisp);
CComQIPtr<IHTMLDocument2, &
IID_IHTMLDocument2> spDocument2(spDisp);
//Now recursivley browse through all of
//IHTMLWindow2 in a doc
RecurseWindows(spDocument2);
bFrameFound = TRUE;
}
}
if(!bFrameFound || !m_bFoungLoginPage)
{
CComPtr<IIHTMLElementCollection> spFrmCol;
CComPtr<IIHTMLElementCollection> spElmntCol;
/*multipe <FORM> object can be in a page,
connect to each one them
You never know which one contains uid and pwd fields
*/
hr = spDocument->get_forms(&spFrmCol);
// get element collection from page to check
if a page is a lgoin page
hr = spDocument->get_all(&spElmntCol);
if(IsLoginPage(spElmntCol))
EnableEvents(spFrmCol);
}
}
如果页面有密码字段,那么您将对获取用户 ID 和密码感兴趣。
BOOL CIeLoginHelper::IsLoginPage(CComPtr<IHTMLElementCollection>&spElemColl)
{
if(spElemColl == NULL)
return m_bFoungLoginPage;
_variant_t varIdx(0L, VT_I4);
long lCount = 0;
HRESULT hr = S_OK;
hr = spElemColl->get_length (&lCount);
if (SUCCEEDED(hr))
{
for(long lIndex = 0; lIndex <lCount; lIndex++ )
{
varIdx=lIndex;
CComPtr<IDispatch>spElemDisp;
hr = spElemColl->item(varIdx, varIdx, &spElemDisp);
if (SUCCEEDED(hr))
{
CComPtr<IHTMLInputElement> spElem;
hr = spElemDisp->QueryInterface(IID_IHTMLInputElement, (void**)&spElem);
if (SUCCEEDED(hr))
{
_bstr_t bsType;
hr = spElem->get_type(&bsType.GetBSTR());
if(SUCCEEDED(hr) && bsType.operator==(L"password"))
{
m_bFoungLoginPage = true;
}
}
}
if(m_bFoungLoginPage)
return m_bFoungLoginPage;
}
}
return m_bFoungLoginPage;
}
一旦确定了目标页面,您所要做的就是遍历表单集合并连接到表单元素的事件,如下所示
_variant_t varIdx(0L, VT_I4);
long lCount = 0;
HRESULT hr = S_OK;
hr = pElemColl->get_length (&lCount);
if (SUCCEEDED(hr))
{
for(long lIndex = 0; lIndex <lCount; lIndex++ )
{
varIdx=lIndex;
hr=pElemColl->item(varIdx, varIdx, &pElemDisp);
if (SUCCEEDED(hr))
{
hr = pElemDisp->QueryInterface(IID_IHTMLFormElement, (void**)&pElem);
if (SUCCEEDED(hr))
{
// Obtained a form object.
IConnectionPointContainer* pConPtContainer = NULL;
IConnectionPoint* pConPt = NULL;
// Check that this is a connectable object.
hr = pElem->QueryInterface(IID_IConnectionPointContainer,
(void**)&pConPtContainer);
if (SUCCEEDED(hr))
{
// Find the connection point.
hr = pConPtContainer->FindConnectionPoint(
DIID_HTMLFormElementEvents2, &pConPt);
if (SUCCEEDED(hr))
{
// Advise the connection point.
// pUnk is the IUnknown interface pointer for your event sink
hr = pConPt->Advise((IDispatch*)this, &m_dwBrowserCookie);
pConPt->Release();
}
}
pElem->Release();
}
pElemDisp->Release();
}
}
}
捕获用户 ID 和密码
数据捕获的时机很重要。最佳时机是在提交表单时。表单可以通过多种方式提交
以上任何对象都将触发事件 DISPID_HTMLFORMELEMENTEVENTS2_ONSUBMIT
。
在这种情况下,我们必须处理
- 当类型为
<INPUT TYPE=submit>
或<INPUT TYPE=image>
或<BUTTON TYPE=submit>
的对象被鼠标左键单击,或按下 Enter 键或空格键时。 - 通过在对象的鼠标或键盘事件处理程序中调用
form.submit
。DISPID_HTMLELEMENTEVENTS2_ONKEYPRESS
和DISPID_HTMLELEMENTEVENTS2_ONCLICK
一旦您知道何时捕获数据,剩下的就非常简单了。您所要做的就是遍历元素集合并检索用户 ID 和密码。
_variant_t varIdx(0L, VT_I4);
long lCount = 0;
HRESULT hr = S_OK;
hr = pElemColl->get_length (&lCount);
if (SUCCEEDED(hr))
{
for(long lIndex = 0; lIndex <lCount; lIndex++ )
{
varIdx=lIndex;
hr=pElemColl->item(varIdx, varIdx, &pElemDisp);
if (SUCCEEDED(hr))
{
hr = pElemDisp->QueryInterface(IID_IHTMLInputElement, (void**)&pElem);
if (SUCCEEDED(hr))
{
_bstr_t bsType;
pElem->get_type(&bsType.GetBSTR());
if(bsType.operator ==(L"text"))
{
pElem->get_value(&bsUserId.GetBSTR());
}
else if(bsType.operator==(L"password"))
{
pElem->get_value(&bsPassword.GetBSTR());
}
pElem->Release();
}
pElemDisp->Release();
}
if(bsUserId.GetBSTR() && bsPassword.GetBSTR() &&
( bsUserId.operator!=(L"") && bsPassword.operator!=(L"") ) )
{
return;
}
}
}
历史
- V1.0.0.1 - 第一个版本。
- V1.0.1.1 - 上传于 2006 年 8 月 29 日。此版本枚举页面中的框架以找出任何框架是否具有登录页面。