Por qué la programación de ganchos tiene un poder tan letal, amemos y odiemos

1. Hook presenta el mecanismo de implementación de hook

El nombre en inglés del gancho es Hook, que es una tecnología para interceptar mensajes de una determinada aplicación o de todos los procesos del sistema Windows. La siguiente figura muestra el proceso de entrega de mensajes por una aplicación de Windows:

为ä½é © åç¼ç¨å · æå¦æ¤å¤§çæ伤åï¼è® © æä »¬åç ± åæ¨

 

Si presiona una tecla en el teclado, el sistema operativo recibirá el mensaje de presión de la tecla, colocará el mensaje en la cola de mensajes, y luego la cola de mensajes enviará el mensaje a la aplicación correspondiente, y luego lo enviará al sistema operativo después de siendo procesado por la aplicación., El sistema operativo entonces llama al procedimiento de ventana creado por la aplicación correspondiente.

        Podemos interceptar estos mensajes a través de ganchos, de modo que los mensajes no se transmitan, o hacer algo después de interceptar los mensajes de interés.

1.2 Clasificación e implementación del gancho

        Los enganches se dividen en enganches en proceso y enganches globales.Los enganches en proceso interceptan mensajes de un proceso específico y usted puede crear y eliminar directamente enganches en el proceso. El gancho global es interceptar los mensajes de todos los procesos, que se pueden implementar en una biblioteca dinámica.

        Por lo general, hay tres pasos para implementar un gancho. Primero, cree un gancho con una API especial: SetWindowsHookEx. Después de que la creación sea exitosa, el mensaje se pasará a la función de procesamiento especificada por el parámetro formal de SetWindowsHookEx. Luego analice el mensaje recibido en la función de procesamiento de mensajes y realice el procesamiento correspondiente. Finalmente, después de que se agote el gancho, use la API (UnhookWindowsHookEx) para destruir el gancho.

Segundo, crea un gancho

2.1 Creación de gancho

SetWindowsHookEx instala un proceso de enganche definido por la aplicación y coloca el proceso de enganche creado en la cadena de enganches. Se pueden instalar múltiples enganches y varios enganches forman una cadena de enganches. El último enganche instalado siempre está en la parte superior. La función para crear el gancho es la siguiente:

为ä½é © åç¼ç¨å · æå¦æ¤å¤§çæ伤åï¼è® © æä »¬åç ± åæ¨

 

Cree un gancho, devuelva el asa del gancho, de lo contrario devuelva NULL. Los parámetros formales se definen de la siguiente manera:

idHook: tipo de proceso de gancho, como: gancho de mensaje del mouse, gancho de mensaje de teclado, gancho de monitoreo de cola de mensajes, etc. Los valores específicos son los siguientes:

为ä½é © åç¼ç¨å · æå¦æ¤å¤§çæ伤åï¼è® © æä »¬åç ± åæ¨

 

lpfn: El proceso de enlace correspondiente, es decir, el nombre de una función de devolución de llamada que procesa mensajes. Si el parámetro dwThreadId es 0, o dwThreadId apunta a un identificador de hilo creado por otro proceso, entonces lpfn debe apuntar a un enlace ubicado en una dinámica proceso de biblioteca. En otros casos, lpfn puede apuntar a un proceso de enlace en este proceso.

hMod: apunta al identificador de instancia de la aplicación donde se encuentra el procedimiento de enlace , como el identificador de la DLL donde se encuentra el procedimiento de enlace. Si el subproceso especificado por dwThreadId es creado por el proceso actual y el proceso de enlace está en el proceso actual, entonces hMod debe establecerse en NULL.

dwThreadId: especifique el indicador de hilo asociado con el gancho. Si es 0, entonces el enlace estará relacionado con todos los subprocesos que se ejecutan en el escritorio.

 El proceso de enlace puede estar relacionado con un hilo específico o con todos los hilos, dependiendo del valor de dwThreadId.

2.2 Análisis del proceso de gancho

        El proceso de enlace es una función de devolución de llamada que procesa el mensaje interceptado por el enlace, es decir, el segundo parámetro de la función SetWindowsHookEx. El formato es el siguiente:

LRESULT CALLBACK MouseProc (int nCode, WPARAM wParam, LPARAM lParam); Los significados de los parámetros formales no son todos iguales, y los significados de los parámetros formales de diferentes procedimientos de gancho son diferentes. Tomemos el gancho del mouse como ejemplo. Los significados de los parámetros son los siguientes:

nCode: instruye al proceso de gancho cómo manejar el mensaje actual. Si el gancho es un gancho de mensaje del mouse, tiene dos significados:

为ä½é © åç¼ç¨å · æå¦æ¤å¤§çæ伤åï¼è® © æä »¬åç ± åæ¨

 

 wParam: indica la bandera de mensaje del mouse.

lParam: puntero a la estructura MOUSEHOOKSTRUCT. La siguiente es la estructura MOUSEHOOKSTRUCT, que contiene mensajes del mouse.

typedef struct {

   POINT pt; // El cursor contiene x, y, que son las coordenadas de la pantalla.

   HWND hwnd; // El identificador de la ventana de destino

   UINT wHitTestCode;

   ULONG_PTR dwExtraInfo;

} MOUSEHOOKSTRUCT, * PMOUSEHOOKSTRUCT;

A través de estos tres parámetros, el mensaje se puede analizar y procesar.

        El significado de otros parámetros del proceso de enlace se puede ver en detalle a través de documentos MSDN, como KeyboardProc, MessageProc, etc.

2.3 Destruye el gancho

        BOOL UnhookWindowsHookEx (HHOOK hhk); La función de esta API es eliminar el gancho creado por SetWindowsHookEx de la cadena de ganchos. El parámetro formal es el identificador de gancho devuelto por SetWindowsHookEx.

Tres ganchos en proceso

        Los enganches en proceso se utilizan generalmente para interceptar mensajes de la aplicación actual. Por lo general, se pueden crear cuando se inicializa la aplicación. Por supuesto, también se pueden crear cuando se desea crear, pero los mensajes anteriores a la creación no se pueden interceptar.

3.1 Función de procedimiento de gancho del mouse

El siguiente es el código de proceso del gancho del mouse:

HWND            g_DestWndH = NULL;
HHOOK          g_hHookDm = NULL;
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{            //此区间内的消息都是鼠标消息
             if (WM_MOUSEMOVE <= wParam && wParam <= WM_MOUSEWHEEL) {
                           if(g_DestWndH != NULL) {
                                         ::SendMessage(g_DestWndH, wParam, wParam, lParam);
                                         //鼠标钩子,lParam是MOUSEHOOKSTRUCT结构指针
                                         PMOUSEHOOKSTRUCT lpMsg = (PMOUSEHOOKSTRUCT)lParam;
                                         lpMsg->hwnd = NULL;    //把目的窗口置NULL
                                         lpMsg->dwExtraInfo = 0L;
                                         lpMsg->pt = CPoint(0, 0);
                                         lpMsg->wHitTestCode = 0L;
                           }
                           return 1;//如果想让此消息不再往下传,返回非0
             }
             else//不感兴趣的消息往下传
                           return ::CallNextHookEx(g_hHookDm, nCode, wParam, lParam);
}

3.2 Crear proceso de gancho

        El siguiente es el código para crear el proceso de enlace:

if(g_hHookDm == NULL) {
             //如果dwThreadId指定的线程是由当前进程创建,并且钩子过程在当时进程中,那么hMod必须设置为NULL。
             g_hHookDm = ::SetWindowsHookEx(WH_MOUSE,MouseProc, NULL, GetCurrentThreadId());
             if (NULL != g_hHookDm)//创建成功
                           g_DestWndH = m_hWnd;//保存目的窗口句柄
}

3.3 Destruir los ganchos en proceso

        Tome el proceso de enlace de la cadena de enlace y llame a una API:

if (NULL != g_hHookDm) {
             UnhookWindowsHookEx(g_hHookDm);
             g_DestWndH = NULL;
}

En este punto, se ha completado todo el proceso de creación y ejecución de ganchos en el proceso para destruirlos.

Cuatro, gancho global

4.1 Función de procedimiento de gancho del teclado

Los enlaces en proceso anteriores son solo para interceptar mensajes de este proceso. Si desea interceptar mensajes de todos los hilos del escritorio, debe pasar el enlace global. El gancho global debe implementarse en la DLL. El proceso de gancho no se puede implementar en el código de este proceso, por lo que primero debe escribir una DLL. Para el código, consulte "HookDll". Tome el gancho del teclado como ejemplo:

 

nCode: Tiene el mismo significado que el gancho del mouse y tiene dos valores, HC_ACTION y HC_NOREMOVE.

Param: representa una clave virtual específica, como: VK_TAB y VK_RETURN representan la tecla Tab y la tecla Enter respectivamente. Para otros mensajes de clave virtual, puede ver el "código de clave virtual" de MSDN.

lParam: almacena información extendida, como: datos de repetición de teclas, 29 bits representan la presión de la tecla ALT.

El significado específico de los parámetros se puede encontrar en MSDN. Con respecto a la cuestión de las combinaciones de teclas, consulte el siguiente código de muestra de la función de gancho del teclado:

HHOOK g_HookKeyBoard = NULL;
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
             //wParam表示虚拟键,lparam第29位表示ALT键按下状态(按下为1,否则为0),
             //GetKeyState可以获得对应键的状态,所以收下表示Control + ALT + Z组合键按下
             if ('Z' == wParam && GetKeyState(VK_CONTROL) < 0 && (lParam >> 29 & 1)) {
                           ::SendMessage(g_DestWnd, WM_KEYDOWN, wParam, lParam);
                           return 1;
             }
             else
                           return CallNextHookEx(g_HookKeyBoard, nCode, wParam, lParam);
}

Nota: En una determinada versión del sistema, en el estado de depuración, si la ventana del depurador no obtiene el foco, no permanecerá en el lugar donde se estableció el punto de interrupción. Se quedará solo si obtiene el foco, es decir , en el estado de depuración, no permanecerá en el punto de interrupción. El mensaje que pertenece al proceso no permanecerá en el punto de interrupción.

4.2 Crear una interfaz de enlace global

        El método para crear un enlace global está en la DLL, por lo que se debe derivar una interfaz. El código es el siguiente:

BOOL SetHook(HWND hWnd){
             if (NULL == hWnd)
                           return FALSE;
             g_DestWnd = hWnd;
             //第三个参数可通过些方法获得:GetModuleHandle("MouseHook.dll")
             return (NULL != (g_HookKeyBoard = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInst, 0)));
}

Su parámetro formal es un identificador de ventana, que indica el identificador de la ventana a la que se debe transmitir el mensaje de interés interceptado. El tercer parámetro de MetWindowsHookE es el identificador de instancia de la DLL, que se puede obtener a través del parámetro entrante de DllMain, o mediante el método GetModuleHandle. Obtenido, el cuarto parámetro debe ser 0 para obtener todos los mensajes de hilo de escritorio. El éxito devolverá VERDADERO de lo contrario FALSO.

4.3 Destruye la interfaz de enlace global

        En la DLL, se debe introducir una interfaz para destruir el gancho global. El código es el siguiente:

void DestroyHook(){
             if (NULL != g_HookKeyBoard) {
                           UnhookWindowsHookEx(g_HookKeyBoard);
                           g_DestWnd = NULL;
             }
}

4.4 Use DLL para crear un gancho global

        La siguiente es la implementación de la creación y destrucción de ganchos globales, el código es el siguiente:

HINSTANCE hIst = NULL;
void CHookTestDlg::OnCreateDllHook()
{
             hIst = ::LoadLibrary("..\\HookDll\\Debug\\HookDll.dll");
             if (NULL != hIst) {
                           typedef BOOL (*pFunSetHook)(HWND);
                           pFunSetHook pSetHook = (pFunSetHook)GetProcAddress(hIst, "SetHook");
                           if (NULL != pSetHook) {
                                         if(pSetHook(m_hWnd))
                                                       AfxMessageBox("创建全局钩子成功...");
                           }
             }
}
void CHookTestDlg::OnDestroyDllHook()
{
             if (NULL != hIst) {
                           typedef void (*pFunDestroyHook)();
                           pFunDestroyHook pDestryHook = (pFunDestroyHook)GetProcAddress(hIst, "DestroyHook");
                           if (NULL != pDestryHook) {
                                         pDestryHook();
                                         ::FreeLibrary(hIst);
                                         hIst = NULL;
                           }
             }
}

Tipo de anzuelo

(1) El gancho del teclado y el gancho del teclado de bajo nivel pueden monitorear varios mensajes del teclado.

(2) El gancho del mouse y el gancho del mouse de bajo nivel pueden monitorear varios mensajes del mouse.

(3) Los ganchos de Shell pueden monitorear varios mensajes de eventos de Shell. Como iniciar y cerrar aplicaciones.

(4) El enlace de registro puede registrar varios mensajes de eventos tomados de la cola de mensajes del sistema.

(5) El enlace de procedimiento de ventana monitorea todos los mensajes enviados desde la cola de mensajes del sistema a la ventana de destino.

 

13 tipos de gancho de uso común:

WH_CALLWNDPROC y WH_CALLWNDPROCRET Hooks: monitorean los mensajes enviados al procedimiento de ventana.

WH_CBT Hook: antes de los siguientes eventos, el sistema llamará a la subrutina WH_CBT Hook, estos eventos incluyen:

1. Eventos de ventana como activar, crear, destruir, minimizar, maximizar, mover, cambiar de tamaño, etc .;

2. Instrucciones completas del sistema;

3. Mueva los eventos del mouse y del teclado de la cola de mensajes del sistema;

4. Establezca el evento de enfoque de entrada;

5. Sincronice los eventos de la cola de mensajes del sistema.

El valor de retorno de la subrutina Hook determina si el sistema permite o evita una de estas operaciones.

Gancho WH_DEBUG

Antes de que el sistema llame a la subrutina Hook asociada con otros Hooks en el sistema, el sistema llama a la subrutina Hook WH_DEBUG. Puede utilizar este Hook para decidir si permite que el sistema llame a las subrutinas Hook asociadas con otros Hooks.

WH_FOREGROUNDIDLE Gancho

Cuando el subproceso de primer plano de la aplicación está inactivo, puede utilizar el gancho WH_FOREGROUNDIDLE para realizar tareas de baja prioridad. Cuando el hilo de primer plano de la aplicación se vuelve inactivo, el sistema llama a la subrutina de gancho WH_FOREGROUNDIDLE.

WH_GETMESSAGE Gancho

La aplicación usa WH_GETMESSAGE Hook para monitorear el mensaje devuelto por la función GetMessage o PeekMessage. Puede usar WH_GETMESSAGE Hook para monitorear la entrada del mouse y el teclado, y otros mensajes enviados a la cola de mensajes.

WH_JOURNALPLAYBACK Hook: permite a las aplicaciones insertar mensajes en la cola de mensajes del sistema.

WH_JOURNALRECORD Hook: supervise y registre eventos de entrada, registre eventos continuos de mouse y teclado y reproducción.

Gancho WH_KEYBOARD: supervisa los mensajes WM_KEYDOWN y WM_KEYUP en la aplicación;

WH_KEYBOARD_LL Hook: monitorea los mensajes del teclado ingresados ​​en la cola de mensajes del hilo.

Gancho WH_MOUSE: supervisa el mensaje del mouse devuelto por la función GetMessage o PeekMessage.

WH_MOUSE_LL Hook: supervisa la entrada de mensajes del mouse en la cola de mensajes del hilo.

WH_MSGFILTER y WH_SYSMSGFILTER Hooks: monitorea menús, barras de desplazamiento, cuadros de mensaje, mensajes de diálogo y descubre que el usuario usa la combinación de teclas ALT + TAB o ALT + ESC para cambiar de ventana.

WH_SHELL Hook: las aplicaciones Shell pueden usar WH_SHELL Hook para recibir notificaciones importantes. Cuando se activa la aplicación de shell y cuando se crea o destruye la ventana de nivel superior, el sistema llama a la subrutina WH_SHELL Hook.

Hay 5 situaciones para WH_SHELL:

Siempre que se genere, active o destruya una ventana sin propietario de nivel superior;

Cuando la barra de tareas necesita volver a dibujar un botón;

Cuando el sistema necesita mostrar la forma minimizada de un programa sobre la barra de tareas;

Cuando cambia el estado actual de distribución del teclado;

Cuando el usuario presiona Ctrl + Esc para ejecutar el Administrador de tareas (o el programa del mismo nivel).

Por convención, las aplicaciones de shell no reciben mensajes WH_SHELL.

Por lo tanto, antes de que la aplicación pueda recibir el mensaje WH_SHELL, la aplicación debe llamar a la función SystemParametersInfo para registrarse.

Los ganchos son muy poderosos, debe tener cuidado al usarlos ~ Si necesita probar la letalidad de los ganchos, puede agregar grupos: ①①⑤①③⑨⑤⑨⑦⑤Obtenerlo

El video de cómo hacer la programación del gancho se ha subido a la página de inicio, puedes verlo

Supongo que te gusta

Origin blog.csdn.net/Python6886/article/details/111708872
Recomendado
Clasificación