引言
在Windows下輸入密碼時,為了安全起見通常都以星號來屏蔽使用者輸入的密碼。如果需要在自己編寫的應用程式中需要使用者輸入密碼,通常的選擇就是選用Edit控制項並設定其Password屬性。雖然Edit控制項的Password屬性在一定程度上可以保護使用者的密碼不被視覺偷窺,但在許多窺探密碼的駭客軟體面前卻不起任何作用,密碼將赫然暴露於攻擊者面前。顯然僅僅依靠Edit控制項本身的Password功能已不能勝任保護使用者密碼的作用了,這就需要我們根據此類駭客軟體的攻擊方式來採取相應的措施來阻止密碼被非法擷取。本文將首先 分析非法擷取Password的原理及工作方式,然後給出用VisualC++6.0來實現保護Edit控制項中的 Password不被非法擷取的對策。
非法擷取Password的原理
Edit控制項是Windows的一個常用標準控制項,當把其Password屬性設為True時,就會將輸入的內容屏蔽為星號,從而達到保護的目的。雖然我們看來都是星號,但程式中的Edit控制項實際仍是使用者輸入的密碼,應用程式可以擷取該控制項中的密碼,其他應用程式也可以通過某種技術手段來非法擷取Edit控制項中的內容。其中最簡單的一個方法就是從外部程式向該編輯控制項發出一個取Edit控制項內容的訊息WM_GETTEXT或EM_GETLINE就能夠輕鬆得到想要的內容。駭客程式正是利用Edit控制項的這個特性,當發現當前探測的視窗是Edit控制項並且具有ES_PASSWORD屬性時,則通過SendMessage向此視窗發送WM_GETTEXT或EM_GETLINE訊息,這樣Edit框中的內容就一目瞭然了。
駭客軟體工作方法:
黑軟在工作時,首先要確定攻擊目標,由於按鈕控制項、下拉式方塊控制項等根本就不可能是接受密碼輸入的控制項,因此要首先擷取當前待檢測的視窗控制代碼,並由此進一步判斷是否是Edit控制項,這一般是通過滑鼠來指定要探測的視窗,並可在WM_MOUSEMOVE訊息的響應函數中進行判斷。如果當前視窗被檢測為編輯控制項,則還需進一步擷取其視窗風格以判斷該編輯框是否是設定了Password屬性的密碼輸入框,如果得到確認則向其發送WM_GETTEXT或EM_GETLINE訊息。為清楚起見,下面給出這部分代碼的詳細實現:
//將客戶座標轉換成螢幕座標
ClientToScreen(&point);
//返回一個包含指定螢幕座標點的視窗
CWnd* pWnd = CWnd::WindowFromPoint(point);
if (pWnd)
{
//擷取視窗控制代碼
HWND hwndCurr = pWnd->GetSafeHwnd();
if ((::GetWindowThreadProcessId (GetSafeHwnd(), NULL)) != (::GetWindowThreadProcessId (hwndCurr, NULL)))
{
char lpClassName[255];
//擷取類名
if (::GetClassName(hwndCurr, lpClassName, 255))
{
//判斷是否是Edit控制項
if (0 == m_strWndClass.CompareNoCase("EDIT"))
{
//擷取視窗風格
LONG lStyle = ::GetWindowLong(hwndCurr, GWL_STYLE);
//如果設定了ES_PASSWORD屬性
if (lStyle & ES_PASSWORD)
{
char szText[255];
//通過控制代碼hwndCurr向此控制項發送WM_GETTEXT訊息
::SendMessage(hwndCurr, WM_GETTEXT, 255, (LPARAM)szText);
//密碼已儲存在szText中
m_strPassword = szText;
}
}
}
}
}
上述代碼中值得注意的有以下幾個關鍵地方:
ClientToScreen(&point);
CWnd* pWnd = CWnd::WindowFromPoint(point);
HWND hwndCurr = pWnd->GetSafeHwnd();
這三句代碼可以擷取當前滑鼠位置所在視窗的視窗控制代碼,該視窗控制代碼在調用SendMessage()函數發送訊息時將要用到:
::SendMessage(hwndCurr, WM_GETTEXT, 255, (LPARAM)szText);
這便是真正起作用的SendMessage了,其第一個參數指定了要接收訊息的視窗控制代碼,我們已經通過上面的代碼擷取到了,第二個參數就是讓Edit控制項返回字元的WM_GETTEXT訊息了,並將得到的內容儲存在szText指定的緩衝區中。從而成功完成了一次完整的密碼攻擊行動。
防範措施
既然我們已經徹底分析清楚了此類駭客軟體普遍採取的攻擊手法,那麼我們自然就能制訂出一套防範其攻擊的措施來。下面我們就要對Password進行保護。
首先回顧前面介紹的黑軟攻擊過程,從前面的分析可以看出:Edit控制項的漏洞主要在於沒有對發送WM_GETTEXT或EM_GETLINE訊息者的身份進行檢查,只要能擷取到Edit視窗控制代碼,任何進程都可通過其發送WM_GETTEXT或EM_GETLINE訊息而沒有經過任何形式的身份合法性驗證就輕易騙取到密碼內容。由此可見,為了確保密碼不被非法擷取,應當對訊息寄件者的身份進行合法性驗證,這種驗證的具體實施方法有很多種,這裡僅作為樣本給出一種驗證訊息寄件者身份的方法:
首先建立一個新的、從CEdit繼承下來的子類CPasswordEdit並申明全域變數g_bSenderIdentity以表明訊息寄件者的身份:
BOOL g_bSenderIdentity;
然後重載CWnd類的虛函數DefWindowProc(),在這個回呼函數中進行具體的身分識別驗證處理:
LRESULTCPasswordEdit::DefWindowProc (UINTmessage,WPARAMwParam,LPARAMlParam)
{
//對Edit的內容擷取必須通過以下兩個訊息之一
if((message==WM_GETTEXT) ||(message==EM_GETLINE))
{
//檢查是否為合法
if(!g_bSenderIdentity)
{
//非法擷取,顯示資訊
AfxMessageBox(_T ("報告:正在試圖竊取密碼!"));
return 0;
}
//合法擷取
g_bSenderIdentity=FALSE;
}
return CEdit::DefWindowProc (message,wParam,lParam);
}
接下來在密碼輸入對話方塊中做些處理。在對話方塊中申明一個類成員m_edtPassword:
CPasswordEdit m_edtPassword;
並在對話方塊的初始化函數OnInitDialog()中加入下列代碼,以完成子類化:
m_edtPassword.SubclassDlgItem(IDC_EDIT_PASSWORD,this);
這將控制與新類做關聯。之後要在對話方塊的資料交換函數中將身份設為合法:
void CDlgInput::DoDataExchange (CDataExchange*pDX)
{
//如果擷取資料
//注意:對於CPropertyPage類這裡不需要 if (pDX->m_bSaveAndValidate)條件
if(pDX->m_bSaveAndValidate)
{
g_bSenderIdentity=TRUE;
}
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDlgInput)
DDX_Text (pDX,IDC_EDIT_PASSWORD,m_sPassword);
//}}AFX_DATA_MAP
}
經過這樣的處理,Password輸入框就擁有了合法身份並受到保護。只有本應用程式能夠提取使用者輸入的密碼內容,而其他任何一個駭客軟體也都會因為沒有合法身份而不能擷取其中的任何資訊。
結論:
以上的方法僅針對VC程式,對於其他語言如VB、Delphi等語言,需要藉助VC做一個Password的ActiveX控制項,實現方法與上述方法基本類似。文中給出的全部代碼在Windows 2000 Professional + SP4下由Microsoft VisualC++ 6.0編譯通過。