使用 SendMessage(hWnd, WM_GETTEXT, ...) 或 GetWindowText() 讀取具有 ES_PASSWORD 樣式的編輯框 (密碼框) 時,只能取得遮罩字元 (如 ●、*) 或空字串,無法取得真實明文密碼。
Windows 為了安全性,系統層級攔截 了對密碼編輯框的 WM_GETTEXT 訊息,自動回傳遮罩字元而非真實內容。這是 EDIT 控制項內建行為,繞過應用程式層。
// 1. 取得當前視窗樣式
LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE);
// 2. 暫時移除 ES_PASSWORD (0x0020)
SetWindowLongPtr(hWnd, GWL_STYLE, style & ~ES_PASSWORD);
// 3. 更新視窗非用戶區 (可選,視情況)
SetWindowPos(hWnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
// 4. 讀取真實文字
TCHAR buffer[256];
SendMessage(hWnd, WM_GETTEXT, 256, (LPARAM)buffer);
// 或 GetWindowText(hWnd, buffer, 256);
// 5. 恢復 ES_PASSWORD 樣式
SetWindowLongPtr(hWnd, GWL_STYLE, style);
SetWindowPos(hWnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
// 6. 使用 buffer 中的明文密碼
// ...
#include
#include
bool GetPasswordEditText(HWND hEdit, LPTSTR lpBuffer, int nMaxCount) {
// 驗證是否為編輯框且有 ES_PASSWORD
TCHAR className[32];
GetClassName(hEdit, className, 32);
if (_tcscmp(className, _T("Edit")) != 0) return false;
LONG_PTR style = GetWindowLongPtr(hEdit, GWL_STYLE);
if (!(style & ES_PASSWORD)) {
// 非密碼框直接讀取
return GetWindowText(hEdit, lpBuffer, nMaxCount) > 0;
}
// 暫時移除密碼樣式
SetWindowLongPtr(hEdit, GWL_STYLE, style & ~ES_PASSWORD);
SetWindowPos(hEdit, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
bool success = (GetWindowText(hEdit, lpBuffer, nMaxCount) > 0);
// 恢復樣式
SetWindowLongPtr(hEdit, GWL_STYLE, style);
SetWindowPos(hEdit, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
return success;
}
// 使用範例
TCHAR password[256];
if (GetPasswordEditText(hTargetEdit, password, 256)) {
_tprintf(_T("取得密碼: %s\n"), password);
} else {
_tprintf(_T("讀取失敗\n"));
}
| 方法 | 原理 | 優點 | 缺點/風險 |
|---|---|---|---|
| 移除 ES_PASSWORD (標準) | 暫時改變控制項樣式 | 官方文件支援、相容性好、無需注入 | 極短時間可見明文 (UI 閃爍)、需 UI 執行緒 |
| DLL 注入 + Hook WM_GETTEXT | 攔截訊息返回真實內容 | 不改變 UI 狀態 | 複雜、觸發殺毒軟體、64/32 位元相容問題 |
| ReadProcessMemory 讀取控制項記憶體 | 直接讀取目標進程堆疊/堆積 | 不需修改樣式 | 極不穩定、結構未文件化、違反安全原則 |
| UI Automation (UIA) | 使用 TextPattern.GetText() | 現代、無障礙標準 | 需目標應用支援 UIA、Vista+、部分密碼框仍遮罩 |
SetWindowLongPtr 必須在建立視窗的執行緒呼叫。若從其他進程注入,需用 SendMessageTimeout 配合 WM_GETTEXT 但仍受 ES_PASSWORD 限制。GetLastError() = 5 Access Denied)。SecureZeroMemory(buffer, sizeof(buffer)),避免被記憶體掃描工具讀取。