首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Win32 API:獲取 ES_PASSWORD 編輯框文字的正確方法

     編輯:程式設計綜合問答

問題現象

使用 SendMessage(hWnd, WM_GETTEXT, ...) 或 GetWindowText() 讀取具有 ES_PASSWORD 樣式的編輯框 (密碼框) 時,只能取得遮罩字元 (如 ●、*) 或空字串,無法取得真實明文密碼。

原因分析

Windows 為了安全性,系統層級攔截 了對密碼編輯框的 WM_GETTEXT 訊息,自動回傳遮罩字元而非真實內容。這是 EDIT 控制項內建行為,繞過應用程式層。

標準解決方案:暫時移除 ES_PASSWORD 樣式

// 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+、部分密碼框仍遮罩

重要注意事項

  1. 執行緒親和性: SetWindowLongPtr 必須在建立視窗的執行緒呼叫。若從其他進程注入,需用 SendMessageTimeout 配合 WM_GETTEXT 但仍受 ES_PASSWORD 限制。
  2. 權限: 目標進程若以較高權限執行 (UAC),低權限進程無法修改其視窗樣式 (會失敗返回 0,GetLastError() = 5 Access Denied)。
  3. 安全性警示: 明文密碼僅存在記憶體瞬間,用完立即清零 SecureZeroMemory(buffer, sizeof(buffer)),避免被記憶體掃描工具讀取。
  4. Chrome/Electron/CEF 等現代框架: 其密碼框非標準 Win32 Edit 控制項,上述方法無效,需用框架自有 API 或 UIA。

參考資源

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved