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

利用 Hook 自動關閉 IE 廣告彈出視窗:WH_CALLWNDPROC 實戰

     編輯:VC 教學

廣告視窗特徵分析

  • 同屬 IEFrame 視窗類別。
  • 無選單列、無工具列 (Chrome-less)。
  • 子視窗結構:IEFrame → WorkerW (內容區),WorkerW 可見性為隱藏。

解決方案:全域視窗程序鉤子 (WH_CALLWNDPROC)

攔截 WM_SHOWWINDOW 訊息,判斷是否為廣告視窗,符合則發送 WM_CLOSE。

DLL 端:鉤子回調函數


// 共享資料段 (可選,用於傳遞設定)
#pragma data_seg("SHARED")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:SHARED,RWS")

LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode < 0)
        return CallNextHookEx(g_hHook, nCode, wParam, lParam);

    CWPSTRUCT *pCwp = (CWPSTRUCT*)lParam;
    if (!pCwp) return CallNextHookEx(g_hHook, nCode, wParam, lParam);

    // 僅處理 WM_SHOWWINDOW
    if (pCwp->message != WM_SHOWWINDOW)
        return CallNextHookEx(g_hHook, nCode, wParam, lParam);

    // 1. 取得視窗類別名
    TCHAR szClassName[128];
    GetClassName(pCwp->hwnd, szClassName, 128);
    if (_tcsicmp(szClassName, _T("IEFrame")) != 0)
        return CallNextHookEx(g_hHook, nCode, wParam, lParam);

    // 2. 遍歷子視窗尋找 WorkerW
    HWND hChild = GetWindow(pCwp->hwnd, GW_CHILD);
    bool bIsAd = false;
    while (hChild) {
        GetClassName(hChild, szClassName, 128);
        if (_tcsicmp(szClassName, _T("WorkerW")) == 0) {
            // WorkerW 不可見 = 廣告視窗特徵
            if (!(GetWindowLong(hChild, GWL_STYLE) & WS_VISIBLE)) {
                bIsAd = true;
            }
            break;
        }
        hChild = GetWindow(hChild, GW_HWNDNEXT);
    }

    // 3. 關閉廣告視窗
    if (bIsAd) {
        PostMessage(pCwp->hwnd, WM_CLOSE, 0, 0);
    }

    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

// DLL 導出:安裝/卸載鉤子
extern "C" __declspec(dllexport) BOOL InstallHook()
{
    g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, g_hInstance, 0);
    return g_hHook != NULL;
}

extern "C" __declspec(dllexport) void UninstallHook()
{
    if (g_hHook) {
        UnhookWindowsHookEx(g_hHook);
        g_hHook = NULL;
    }
}

BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID)
{
    if (reason == DLL_PROCESS_ATTACH) g_hInstance = hInst;
    return TRUE;
}

EXE 端:載入 DLL 與管理生命週期


class TMainForm : public TForm
{
    HINSTANCE hHookDll;
    typedef BOOL (*PFN_INSTALL)();
    typedef void (*PFN_UNINSTALL)();
    PFN_INSTALL InstallHook;
    PFN_UNINSTALL UninstallHook;

    __fastcall TMainForm(TComponent* Owner) : TForm(Owner)
    {
        hHookDll = LoadLibrary("AdBlockHook.dll");
        if (hHookDll) {
            InstallHook = (PFN_INSTALL)GetProcAddress(hHookDll, "InstallHook");
            UninstallHook = (PFN_UNINSTALL)GetProcAddress(hHookDll, "UninstallHook");
            if (InstallHook) InstallHook();
        }
    }

    __fastcall ~TMainForm()
    {
        if (UninstallHook) UninstallHook();
        if (hHookDll) FreeLibrary(hHookDll);
    }
};

關鍵技術細節

點說明
WH_CALLWNDPROC在視窗程序**執行前**調用,可攔截/修改參數,但不可阻止調用 (需 WH_CALLWNDPROCRET 獲取返回值)
CWPSTRUCT包含 hwnd, message, wParam, lParam,即原始訊息參數
共享資料段讓多進程共享 g_hHook,但本案例僅需單一鉤子實例
PostMessage vs SendMessagePostMessage 非同步,避免在鉤子中阻塞 UI 執行緒
64 位 IE需編譯 64 位 DLL,或啟用 32 位 IE (已淘汰)

現代瀏覽器適用性

  • ❌ Chrome/Edge/Firefox:不使用 IEFrame/WorkerW 結構,此法無效。
  • ✅ 嵌入 IE 控制項的應用 (WebBrowser 控制項):同樣有效。
  • 🔄 現代方案:瀏覽器擴展 (Manifest V3) + content script 攔截 window.open / target=_blank。

法律與道德提醒

  • 自動關閉廣告可能違反網站服務條款。
  • 建議改為「攔截通知讓使用者決定」或支援白名單機制。
  • 尊重內容創作者收入來源,合理使用廣告攔截。

IE 廣告攔截 Hook 流程圖

這是一個經典的「鉤子 + 視窗特徵判斷」案例,展示了 WH_CALLWNDPROC 在視窗生命週期早期介入的威力。

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