首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

視窗子類化技術實戰:隱藏資源管理員中特定系統圖示(VPN 連線)

     編輯:關於VC++

視窗子類化是 Windows GUI 程式設計核心技術,可攔截、修改視窗訊息行為。本文實作一個 DLL (Explorer.dll),注入 explorer.exe 進程,子類化 SysListView32 控制項,過濾隱藏包含 "VPN" 字串的圖示(不分大小寫),達到隱藏特定網路連線圖示的目的。

三大核心 API 比較

API作用範圍說明
SetWindowLong單一視窗實例修改特定視窗的視窗程序
SetClassLong進程內同類別所有視窗修改視窗類別的視窗程序(User32 類別,非 MFC/VCL 封裝類)
CallWindowProc呼叫原視窗程序處理完自訂邏輯後轉交原程序,保留控制項原生行為

本文採用 SetClassLong(GCL_WNDPROC) 修改 SysListView32 類別程序,影響該進程所有列表視圖控制項。超類化(如 Delphi VCL)亦受影響,因其基於 GetClassInfo 取得原類別資訊再註冊新類別。

過濾邏輯實作 (Explorer.cpp)

#define MAGIC_NUMBER 0x20 // 32 項門檻,避免掃描大型資料夾影響效能

WNDPROC g_lpfnSuperClassProc = NULL;

__forceinline void HideItem(HWND hListCtrl, DWORD dwCount) {
    LVITEM li = {0}; char text[50] = {0};
    li.pszText = text; li.cchTextMax = 50;
    for (DWORD i = 0; i < dwCount; i++) {
        CallWindowProc(g_lpfnSuperClassProc, hListCtrl, LVM_GETITEMTEXT, i, (LPARAM)&li);
        if (StrStrI(li.pszText, _T("vpn")) != NULL)
            CallWindowProc(g_lpfnSuperClassProc, hListCtrl, LVM_DELETEITEM, i, 0);
    }
}

LRESULT CALLBACK ListFilterProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    if (uMsg == WM_PAINT) {
        DWORD dwCount = CallWindowProc(g_lpfnSuperClassProc, hwnd, LVM_GETITEMCOUNT, 0, 0);
        if (dwCount < MAGIC_NUMBER) HideItem(hwnd, dwCount);
    }
    return CallWindowProc(g_lpfnSuperClassProc, hwnd, uMsg, wParam, lParam);
}

BOOL InstallListFilter() {
    INITCOMMONCONTROLSEX icc = {sizeof(icc), ICC_WIN95_CLASSES};
    if (!InitCommonControlsEx(&icc)) return FALSE;
    HWND hWnd = CreateWindow(_T("SysListView32"), _T(""), WS_CAPTION|LVS_REPORT, 0,0,0,0, NULL,NULL,NULL,NULL);
    g_lpfnSuperClassProc = (WNDPROC)GetClassLong(hWnd, GCL_WNDPROC);
    SetClassLong(hWnd, GCL_WNDPROC, (LONG)ListFilterProc);
    DestroyWindow(hWnd);
    return TRUE;
}

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason, LPVOID lpReserved) {
    if (DLL_PROCESS_ATTACH == ul_reason) InstallListFilter();
    return TRUE;
}

DLL 注入器 (InjectDLL.cpp)

// 取得進程 PID
DWORD Process2PID(LPCTSTR lpszProcess) {
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe = {sizeof(pe)};
    Process32First(hSnap, &pe);
    do { if (!lstrcmpi(lpszProcess, pe.szExeFile)) return pe.th32ProcessID; }
    while (Process32Next(hSnap, &pe));
    return 0;
}

// 遠端執行緒注入 LoadLibrary
BOOL InjectDLL(DWORD dwPid, LPCTSTR lpszDll) {
    EnablePrivilege(SE_DEBUG_NAME);
    HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE, FALSE, dwPid);
    LPVOID pAddr = VirtualAllocEx(hProcess, NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(hProcess, pAddr, lpszDll, lstrlen(lpszDll)+1, NULL);
    LPTHREAD_START_ROUTINE pfnLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
    BOOL bRet = CreateRemoteThread(hProcess, NULL, 1024, pfnLoadLibrary, pAddr, 0, NULL);
    CloseHandle(hProcess);
    return bRet;
}

編譯與使用

  1. 分別編譯 Explorer.dll、InjectDLL.exe(VC++ 6.0 SP5,Win2000 測試通過)
  2. 命令列執行:InjectDLL explorer.exe d:\path\explorer.dll(DLL 路徑必須為絕對路徑)
  3. 注入後,explorer.exe 新開啟/建立的視窗生效

效能考量與命名偏好

「Magic Number 32」假設網路連線不超過 32 個,避免掃描大型資料夾(如系統目錄)拖慢重繪。作者偏好「視窗子類化」而非聳動的「視窗劫持」,技術本質相同,術語應精準。

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