레이블이 hooking인 게시물을 표시합니다. 모든 게시물 표시
레이블이 hooking인 게시물을 표시합니다. 모든 게시물 표시

2012-06-13

WH_MOUSE는 글로벌 훅이 불가

마우스 커서가 위치한 창의 Process ID나 Thread ID를 가져오려면 WH_MOUSE를 사용해서 후킹을 해야하는데 잘 동작하지 않아, 검색해봤으나 아무리 찾아도 관련 정보를 찾을 수 없었다.
마우스 후킹된 데이터를 저장하는 스트럭쳐만 MSLLHOOKSTRUCT대신 MOUSEHOOKSTRUCT를 사용하는 소스는 봤는데, 그렇게 하면 그냥 마우스의 x,y 좌표만 얻을 수 있다(POINT pt값).

MOUSEHOOKSTRUCT의 구조( 이걸 사용해봤자.. 메모리에 있는 내용은 MSLLHOOKSTRUCT)
typedef struct tagMOUSEHOOKSTRUCT {
  POINT     pt;
  HWND      hwnd;
  UINT      wHitTestCode;
  ULONG_PTR dwExtraInfo;
} MOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT, *LPMOUSEHOOKSTRUCT;

MSLLHOOKSTRUCT의 구조
typedef struct tagMSLLHOOKSTRUCT {
  POINT     pt;
  DWORD     mouseData;
  DWORD     flags;
  DWORD     time;
  ULONG_PTR dwExtraInfo;
} MSLLHOOKSTRUCT, *PMSLLHOOKSTRUCT, *LPMSLLHOOKSTRUCT;



그나마 microsoft의 페이지에서 WH_MOUSE로 후킹하는 자료를 찾았으나, 내용은 아래처럼 글로벌 훅이 지원되지 않는다는 말만 있고, 훅이 글로벌 훅이 아닌 Form1에서만 동작했다.
WH_MOUSE만 글로벌 훅이 안된다고 하면 그러려니 할탠데 글로벌 훅 자체가 안된다고 하니, 다른 방법이 있지 않을까 한다.
WH_MOUSE_LL, WH_KEYBOARD_LL은 글로벌 훅이 동작하니까..



이하 MS의 글로벌 후크가 지원되지 않는 다는 내용.

http://support.microsoft.com/kb/318804/ko

글로벌 후크가 .NET Framework에서 지원되지 않음

Microsoft .NET Framework에서는 글로벌 후크를 구현할 수 없습니다. 글로벌 후크를 설치하려면 일관성 있고 유효한 함수를 호출해야 하는 다른 프로세스에 DLL 자체를 삽입하는 기본 동적 연결 라이브러리(DLL) 내보내기가 후크에 있어야 합니다. 이러한 DLL 내보내기는 .NET Framework에서 지원되지 않습니다. 이러한 함수 포인터는 동적으로 만들어지는 프록시이기 때문에 관리되는 코드에는 함수 포인터에 대해 일관성 있는 값이라는 개념이 없습니다.

2012-06-05

Mouse hook

(마우스 커서는 폼 밖에 있으나 x,y 좌표를 계속 받는다.)

풀소스 및 출처 : CodeSummoner, http://www.codeproject.com/Articles/28064/Global-Mouse-and-Keyboard-Library

keyboard와 mouse를 후킹 하는 라이브러리.

지난 번, keyboard hook과의 차이점은

keyboardHook.cs와 mouseHook.cs에서 globalHook.cs을 상속 받아 구현했다.
globalHook.cs에서 필요한 DllImport,
키보드 데이터 스트럭쳐와 마우스 데이터 스트럭쳐 정의,
후킹에 필요한 상수 정의,
후킹이 실행됬는지에 대한 Property정의,
Application.ApplicationExit+= new EventHandler(Application_ApplicationExit)으로 unhook() 함수를 대신하고
후킹의 콜백 프로시져는 HookCallbackProcedure로 가상 함수로 구현되었다.
SetWindowHookEx는 Hook()에서 Start()로 변경되었다.

테스트 실행에서 이미 디버깅되 실행파일로 나와 있는 샘플을 실행할 땐 정상 작동했으나, 직접 F5로 실행해보면 제대로 동작하지 못한다. 중간에 소스코드에 무슨 변화가 있던 것 같다.
임시로 hInstance가 Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]로 되어 있던 것을 LoadLibrary("User32")로 변경하면.. 일단 실행이 된다.

globalHook.cs에 마우스훅스트럭쳐를 보면 마우스 좌표를 기억하기 위한 point형을 사용하고 있다.

        protected class MouseLLHookStruct // MSDN MSLLHOOKSTRUCT와 동일
        {
            public POINT pt;  //마우스 좌표
            public int mouseData; // 메세지가 WM_MOUSEWHEEL일 경우, 휠을 움직인 델타값. X-button일 경우 어떤 X-button인지 저장.
            public int flags; // event injected 플래그
            public int time; // 메시지에 대한 time stamp, 키보드훅과 동일
            public int dwExtraInfo; // additional inform(?), MSDN에는 UintPtr type을 쓴다.
        }


        protected class POINT
        {
            public int x;
            public int y;
        }


mouseHook.cs에 overriding된 훅 프로시져(HookCallbackProcedure)의 내용은 크게 MouseButtons 객체에 후킹된 버튼 기억, 유저 어플리케이션에 전달될 MouseEventArgs 정의, 후킹된 이벤트에 따라 유저 어플리케이션으로 이밴트 발생이 있다.



protected override int HookCallbackProcedure(int nCode, int wParam, IntPtr lParam)
// nCode는 후킹할 대상, WH_MOUSE_LL (=14)

// wParam에는 마우스 메시지가..
// lParam에는 마우스의 훅 정보가 담긴 스트럭쳐의 포인터 (여기서는 MouseLLHookStruct)

MouseButtons button = GetButton(wParam); // 마우스 버튼 객체 정의

// MouseEventArgs 정의
MouseEventArgs e = new MouseEventArgs(
                    button,
                    (eventType == MouseEventType.DoubleClick ? 2 : 1),
                    mouseHookStruct.pt.x,
                    mouseHookStruct.pt.y,
                    (eventType == MouseEventType.MouseWheel ? (short)((mouseHookStruct.mouseData >> 16) & 0xffff) : 0));



        private MouseButtons GetButton(Int32 wParam) // case에 따라  "Left", "Right", "Middle", "None"을 return, MouseEventArgs.Button property에 기록된다.
        {

            switch (wParam)
            {

                case WM_LBUTTONDOWN:
                case WM_LBUTTONUP:
                case WM_LBUTTONDBLCLK:
                    return MouseButtons.Left;
                case WM_RBUTTONDOWN:
                case WM_RBUTTONUP:
                case WM_RBUTTONDBLCLK:
                    return MouseButtons.Right;
                case WM_MBUTTONDOWN:
                case WM_MBUTTONUP:
                case WM_MBUTTONDBLCLK:
                    return MouseButtons.Middle;
                default:
                    return MouseButtons.None;

            }

        }




유저 어플리케이션에서 이 라이브러리를 사용하는 방법은 지난 번 키보드 훅을 사용할 때와 같다.
MouseHook 객체를 생성하고, 생성한 객체의 이밴트에 이밴트핸들러를 추가하고, 어떤 일을 할지 이밴트핸들러를 정의해주면 된다.

using MouseKeyboardLibrary; //네임스페이스 사용
MouseHook mh = new MouseHook(); 마우스 훅 인스턴스 생성


mh.MouseMove += new MouseEventHandler(mh_MouseMove); //MouseHook에 정의된 이벤트에 유저 이벤트핸들러 추가
mh.MouseDown += new MouseEventHandler(mh_mousedown); //마우스 버튼 관련 이밴트 핸들러 추가
mh.Start(); // 마우스 훅 시작.



        private void mh_MouseMove(object sender,MouseEventArgs e) // 이벤트핸들러 구성
        {
            xyCoordinator(e.X, e.Y); //좌표 표시용 함수 호출
            textBox3.Text = mh.IsStarted.ToString(); //test, 후킹 중인지 상태 표시
        }

        private void xyCoordinator(int x, int y) //좌표 표시용 함수
        {
            textBox1.Text = String.Format("x= {0}", x);
            textBox2.Text = String.Format("y= {0}", y);
        }

        private void mh_mousedown(object sender, MouseEventArgs e)
        {
            textBox3.Text = e.Button.ToString(); // "Left" or "Right" 누른 마우스 버튼 표시
        }






2012-05-31

Keyboard hook


글로벌 후킹에 관심이 생겨 자료를 검색하다 보니 좋은 내용을 발견했다.
테스트 해보니 nprotect나 공인인증서에서는 키보드 보안이 user32.dll을 후킹하는 것을 차단하는 것 같다.
그런 보안이 없는 환경에서.. 믿을 만한 PC가 아니라면 중요한 정보는 입력하지 않는게 좋겠다.


후킹 과정 요약.
user32.dll의 SetWindowsHookEx로 후킹
SetWindowsHookEx로 후킹할 때 필요한 콜백함수에 대한 대리자 선언(keyboardHookProc)
후킹한 키 값을 저장할 구조체 선언(keyboardHookStruct)
후킹할 키 값을 List에 등록(HookedKeys)
키보드에 대한 이벤트 추가(KeyDown)
훅 프로시져 구성(hookProc)



출처 및 참고자료:
StormySpike http://www.codeproject.com/Articles/19004/A-Simple-C-Global-Low-Level-Keyboard-Hook
http://www.jiniya.net/wp/microsoftware


// 주석추가만 있고, 원본 수정 및 변경 없음

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices; // dllimport를 하기 위해 InteropServices네임스페이스 추가
using System.Windows.Forms;

namespace Utilities {
///








/// A class that manages a global low level keyboard hook
///

class globalKeyboardHook {
#region Constant, Structure and Delegate Definitions
///








/// defines the callback type for the hook
///

        // 콜백함수에 사용될 대리자 정의
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);

        // 후킹한 키보드의 데이터를 저장할 구조체
public struct keyboardHookStruct {
public int vkCode;  //  virtual-key Code. (range 1~254) dword(4바이트) 이므로 int를 대신 씀
public int scanCode;
public int flags; // bit별로 숫자키패드에서 입력된 값인지, ALT키가 눌린 상태인지, keyboard의 press , release상태 등의 정보가 담긴 플래그
public int time; //
public int dwExtraInfo; // MSDN에서는 UintPtr 형을 쓰라고 함..
}

        ///


        ///  키보드 스트럭쳐에 대한 설명.
        ///  vkCode와 scanCode의 차이점
        ///  vkCode는 code값이 사람이 인식하기 쉬운 알파벳 순서
        ///  scanCode는 code값이 키보드에 위치적인 순서
        ///
        ///     vkCode | scanCode
        ///  a    65   |    30
        ///  b    66   |    48
        ///  c    67   |    46
        ///  d    68   |    32
        ///  --------------------------
        ///  q    81   |    16
        ///  w    87   |    17
        ///  e    69   |    18
        ///  r    82   |    19
        ///
        /// flags- 8bit의 추가 정보 flag
        /// 예) 일반키에 대해서는 down이벤트와 press이벤트일 때 0, up이벤트 일때 128
        /// test에 대한 플래그 코드, 시스템키라던지 확장키인지 플래그에 나타남
        /// 0 bit, 확장키(숫자패드, 펑션키일 때 1)
        /// 1~3 bit, reserved
        /// 4 bit, 이벤트에 inject되었으면 1
        /// 5 bit, context code, Alt키가 눌렸으면 1
        /// 6 bit, reserved
        /// 7 bit, 0= press, 1=release
        ///
        /// time은 타임스탬프, GetMessageTime API에 의해 키가 down, press, up상태마다 타임스탬프가 기록된다.
        /// 예) 163072875, 163077453
        ///
        /// dwExtraInfo
        /// Additional information associated with the message
        ///



const int WH_KEYBOARD_LL = 13; // 후킹할 Handle 상수
const int WM_KEYDOWN = 0x100;  // 후킹할 메시지 상수
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
#endregion

#region Instance Variables
///








/// The collections of keys to watch for
///

        // 모니터링할 키의 List
public List HookedKeys = new List(); //유저 어플리케이션에서 A키와 B키를 후킹할 대상으로 List.Add했다.(ghk.HookedKeys.Add("Keys.A");)
///








/// Handle to the hook, need this to unhook and call the next hook
///

        // hook handle 정의
IntPtr hhook = IntPtr.Zero;
#endregion

#region Events
///








/// Occurs when one of the hooked keys is pressed
///

        public event KeyEventHandler KeyDown;  // KeyDown 이벤트, 사용자 어플리케이션에서 이벤트핸들러를 추가해서 사용. (예: gkh.KeyUp += new KeyEventHandler(gkh_KeyUp);)
///








/// Occurs when one of the hooked keys is released
///

public event KeyEventHandler KeyUp;   // KeyUp 이벤트,
#endregion

#region Constructors and Destructors
///








/// Initializes a new instance of the class and installs the keyboard hook.
///

public globalKeyboardHook() {
hook(); // 후킹을 시작할 함수
}

///








/// Releases unmanaged resources and performs other cleanup operations before the
/// is reclaimed by garbage collection and uninstalls the keyboard hook.
///

~globalKeyboardHook() {
unhook(); // 후킹을 일시정지할 함수
}
#endregion

#region Public Methods
///








/// Installs the global hook
///

public void hook() {
IntPtr hInstance = LoadLibrary("User32"); //User32.dll이 hInstance
            /// HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);
            /// idHook, WH_KEYBOARD_LL이 후킹할 대상
            /// lpfn, hookProc은 콜백함수
            /// hMod, hInstance("user32.dll") 후킹 프로시져가 존재하는 모듈의 핸들
            /// dwThreadId, 후킹할 쓰레드의 ID, 0은 시스템 전역을 뜻함.
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}

///








/// Uninstalls the global hook
///

        // 후킹 일시정지
public void unhook() {
UnhookWindowsHookEx(hhook);
}

///







        /// wParam        /// 키보드 입력 이벤트를 일으킨 가상 키 코드라고 한다...시스템키, 확장키, Down , up 에 따라 값이 변함.        /// lParam        /// 키보드 입력 이벤트에 대한 추가 정보라고 한다...        /// 75819508이 출력되는데 이 정보에 대한 주소        /// 비교) keyboardHookStruct = (KeyBoardHookStruct) Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct))        /// -> lparam의 주소를 참조해서 keyboardHookStructure 객체를 생성한 소스도 있다.        /// lparam의 비트별 정보는 아래를 참조        /// 0~15 bit, 반복횟수        /// 16~23 bit, scanCode        /// 24 bit, is Extended key        /// 25~28 bit, reserved        /// 29 bit, Alt key        /// 30 bit, 이전 키이벤트의 상태가 눌린 상태면 1        /// 31 bit, 지금 상태가 눌린 상태면 1 /// The callback for the keyboard hook
///

///
The hook code, if it isn't >= 0, the function shouldn't do anyting
///
The event type
///
The keyhook event information
///
public int hookProc(int code, int wParam, ref keyboardHookStruct lParam) {
if (code >= 0) {
Keys key = (Keys)lParam.vkCode; // LParam에 후킹한 vkCode. key는 후킹된 키보드 값이다.
if (HookedKeys.Contains(key)) { // HookedKeys는 후킹할 키의 리스트였음. 따라서 이 리스트에 지금 눌린 key값이 있으면 아래 KeyDown, KeyUp 이벤트가 실행된다.
KeyEventArgs kea = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null)) {
KeyDown(this, kea) ; // KeyDown(object sender, KeyEventArgs e)
} else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null)) {
KeyUp(this, kea);
}
if (kea.Handled) //이벤트에 대한 Handled가 true인 경우, 후킹된 메시지는 유저 어플리케이션에서 처리하고 폐기됨. false일 경우 처리하고 그 후에 user32.dll에서 처리하도록 함.
return 1;    //Handled 값이 true여서 해당 키에 대해 유저 어플리케이션에서만 처리되고, 1을 return, user32.dll은 해당 KeyEvent를 못받음.
}
}

            return CallNextHookEx(hhook, code, wParam, ref lParam);  // 다른 후킹 프로시져에게 제어권을 넘겨주는 역할, return 0로 해도 실행은 됨.
}
#endregion

#region DLL imports
        // SetWindowsHookEx, UnhookWindowsHookEx, CallNextHookEx에 대한 DllImport.
///








/// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
///

///
The id of the event you want to hook
///
The callback.
///
The handle you want to attach the event to, can be null
///
The thread you want to attach the event to, can be null
/// a handle to the desired hook
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);

///








/// Unhooks the windows hook.
///

///
The hook handle that was returned from SetWindowsHookEx
/// True if successful, false otherwise
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);

///








/// Calls the next hook.
///

///
The hook id
///
The hook code
///
The wparam.
///
The lparam.
///
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);

///








/// Loads the library.
///

///
Name of the library
/// A handle to the library
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
#endregion
}
}



사용 땐,
// 위 소스의 네임스페이스 사용
using utility;

// globalKeyboardHook 생성
globalKeyboardHook gkh = new globalKeyboardHook();


// HookedKeys 리스트에 모니터링할 키 추가
gkh.HookedKeys.Add(Keys.A);
gkh.HookedKeys.Add(Keys.B);
// 이벤트 추가

gkh.KeyDown += new KeyEventHandler(gkh_KeyDown);
gkh.KeyUp += new KeyEventHandler(gkh_KeyUp);
// 이벤트 처리



void gkh_KeyUp(object sender, KeyEventArgs e) {
lstLog.Items.Add("Up\t" + e.KeyCode.ToString());
e.Handled = true;
}

void gkh_KeyDown(object sender, KeyEventArgs e) {
lstLog.Items.Add("Down\t" + e.KeyCode.ToString());
e.Handled = true;
}