ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C# 키보드 후킹 (샘플)
    프로그래밍/프로그래밍 관련 2012. 6. 3. 18:49

    Just hook!

     

    첨부된 파일을 먼저 확인하시라. 컴파일을 수행하고 Hook! 버튼을 누르면 해당 폼에서뿐만 아니라 전체 윈도우에서 delete, tab, esc 키 등이 입력되지 않는 것을 확인할 수 있다. (alt + tab 등도 tab키가 입력되는 조합이므로 입력되지 않는다)

     

    실제 코드를 살펴보자. (Form1.cs 참조) KeyboardHooker의 사용 방법은 실로 간단한데, 단순히 KeyboardHooker.HookedKeyboardUserEventHandler 딜리게이트를 처리할 수 있는 이벤트를 만들고, 이벤트 핸들러를 KeyboardHooker.HookedKeyboardUserEventHandler에 등록하면 된다.

     

    private void Form1_Load(object sender, System.EventArgs e) {

        this.HookedKeyboardNofity += new KeyboardHooker.HookedKeyboardUserEventHandler(Form1_HookedKeyboardNofity);

    }

     

    event KeyboardHooker.HookedKeyboardUserEventHandler HookedKeyboardNofity;

        이벤트 핸들러를 등록

     

    그리고 이벤트 핸들러를 구현하자. 지금 구현된 이벤트 핸들러는 tab, delete, esc 키의 입력을 막고 있으나, 사용하기에 따라서는 조합키(ctrl + a같은)의 입력을 핸들링하는데 사용하거나, KeyLogger의 용도로써도 사용할 수 있다. 이벤트 핸들러의 구현은 다음과 같다 :

     

    private long Form1_HookedKeyboardNofity(

        bool bIsKeyDown, bool bAlt, bool bCtrl, bool bShift, bool bWindowKey, int vkCode ) {

    long lResult = 0;

     

    //  입력을 막고 싶은 키가 있을 경우, 해당 키가 입력되었을 때

    //  0이 아닌 값을 리턴하면 다른 프로세스가 해당 키보드 메시지를 받지 못하게 된다.

    //  지금의 예처럼 코딩하면 Tab,Delete,Esc 키의 입력을 막게 된다.

    if(

        (vkCode == (int)System.Windows.Forms.Keys.Tab) ||

        (vkCode == (int)System.Windows.Forms.Keys.Delete) ||

        (vkCode == (int)System.Windows.Forms.Keys.Escape)) {

        lResult = -1;

    }

     

    return lResult;

    }

        이벤트 핸들러 구현

     

    실제 후킹을 시작/중지하는 코드는 다음과 같다 :

     

    private void button2_Click(object sender, System.EventArgs e) {

        //  후킹 여부 검사

        if(KeyboardHooker.Hooked)

            // 후킹 중지

            KeyboardHooker.UnHook();

        else

            // 후킹 시작(이벤트를 넘겨줘서, KeyboardHooker에서 해당 이벤트를 이벤트 핸들)

            KeyboardHooker.Hook(HookedKeyboardNofity);

        

        if(KeyboardHooker.Hooked)

            this.button2.Text = "Unhook";

        else

            this.button2.Text = "Hook!";

        this.textBox1.Focus();

    }

         후킹 시작/중지

     

     

    KeyboardHooker.Hook() 메서드

     

    KeyboardHooker 클래스에서 가장 핵심적인 부분이라고 할 수 있다. 일단 소스를 보면 다음과 같다 :

     

    public static bool Hook(HookedKeyboardUserEventHandler callBackEventHandler)

    {

        bool bResult = true;

        m_hDllKbdHook = SetWindowsHookEx(

            (int)WH_KEYBOARD_LL,

            m_LlKbEh,

            Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]).ToInt32(),

            0);

        

        if(m_hDllKbdHook == 0)

        {

            bResult = false;

        }

        // 외부에서 KeyboardHooker의 이벤트를 받을 수 있도록 이벤트 핸들러를 할당함

        KeyboardHooker.m_fpCallbkProc = callBackEventHandler;

        m_Hooked = bResult;

        return bResult;

    }

     

     이 중 실제로 hook chain에 hook procedure (소스에서는 HookKeyboardProc() 메서드)를 등록하는 함수인 SetWindowsHookEx()의 원형은 MSDN을 참고하면 다음과 같다 :

     

     HHOOK SetWindowsHookEx(

        int idHook,     HOOKPROC lpfn,     HINSTANCE hMod,     DWORD dwThreadId );

     

    WH_KEYBOARD_LL은 상수로써, 저수준(low-level)의 키보드 입력 메시지를 후킹하게 되는 것을 나타낸다.

     

    Km_LlKbEh는 HookedKeyboardEventHandler 딜리게이트로써, HookKeyboardProc() 함수를 이벤트 핸들러로 등록하고 있음을 알 수 있다. Kenial도 WinAPI를 호출할 경우 콜백 함수를 어떤 식으로 등록해야 하는지 몰라서 헤맸던 경험이 있는데, 이와 같이 해당 함수를 이벤트 핸들러로 갖는 딜리게이트를 선언한 후에 해당 딜리게이트의 인스턴스를 콜백 함수 인자로 넘겨주면, 닷넷 프레임워크(아마도 System.Runtime.InteropServices)에서 해당 딜리게이트 인스턴스를 변환, 콜백 함수 주소를 넘겨주는 것으로 보인다.

     

    Marshal.GetHINSTANCE() 함수는 현재 모듈의 인스턴스 핸들을 얻는 함수이다.

     

    마지막 0은 hook procedure가 적용될 쓰레드의 id를 나타내는 인자인데, KeyboardHooker는 윈도우 내 모든 프로그램의 키 입력을 후킹하기 위해 만들어졌으므로 0으로 설정한다.

     

    callBackEventHandler는 KeyboardHooker 객체에 이벤트 핸들러를 등록하게 함으로써, 키 입력이 있을 때 Hook() 메서드를 호출한 어플리케이션에 해당 이벤트를 넘겨주기 위해서 등록하는 것이다.

     

    후킹 기능을 제공하는 클래스는 어차피 여러 개의 인스턴스가 생성될 필요는 없다고 판단해서, Kenial은 이 클래스를 전체가 정적 메서드로 구성된 클래스로 만들었다. 하지만 event는 정적 필드(static)로 사용할 수 없으므로, 이처럼 Form에서 event를 선언해서 유저용 이벤트 핸들러를 KeyboardHooker의 정적 필드 이벤트 핸들러에 등록할 수 있는 형태로 만들게 되었다.

     

     

    KeyboardHooker.HookedKeyboardProc() 메서드

     

    이 메서드는 실제로 low-level 키보드 메시지를 처리하는 프로시저로써 hook chain에 등록된다. (hook chain에 대해서는 msdn의 win32 hooks, about hooks 항목을 읽어보기 바란다)

     

    C# 언어의 (WinAPI 프로그래밍을 하기 위해서) 중요한 키워드 중 하나인 unsafe가 나온다. unsafe 키워드를 처음 접한다면, 여기에서는 단지 포인터를 사용하기 위해서 필요한 것이라고 이해하시고 넘어가면 되겠다.(CopyMemory로 lParam으로 넘어온 KBDLLHOOKSTRUCT의 주소에 접근해 해당 객체를 m_KbDllHs로 복사하고 있다.

     

    그 다음 Hook()에서 등록한 이벤트 핸들러인 m_fpCallbkProc으로 키보드 메시지를 넘겨주게 되어 콜백 함수를 호출하는 것과 같은 형태의 코드가 되었다.

     

    그리고 CallNextHookEx()은 hook chain에 등록된 다른 hook procedure를 호출하는 명령인데, msdn에는 다음과 같이 설명되어 있다 :

     

    If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.

    If nCode is greater than or equal to zero, and the hook procedure did not process the message, it is highly recommended that you call CallNextHookEx and return the value it returns; otherwise, other applications that have installed WH_KEYBOARD_LL hooks will not receive hook notifications and may behave incorrectly as a result. If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure.

     

    nCode가 HC_ACTION이 아닐 때, (low level 키보드 메시지가 아닌 다른 후킹된 메시지일 때) 다음 hook procedure로 해당 메시지를 넘겨줌으로써 hook chain이 제대로 동작할 수 있도록 하는 코드이다.

     

     

    참고

     

    GotDotNet User Sample : Low Level Keyboard Capture

    http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=55e8d5b3-0e53-47eb-99a9-ee65ed45f251

    Key Support, Keyboard Scan Codes, and Windows

    http://www.microsoft.com/whdc/device/input/Scancode.mspx





    출처 - http://blog.naver.com/PostView.nhn?blogId=hpmovie&logNo=120095398849

    댓글