引言
在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編譯通過。