글로벌 후킹에 관심이 생겨 자료를 검색하다 보니 좋은 내용을 발견했다.
테스트 해보니 nprotect나 공인인증서에서는 키보드 보안이 user32.dll을 후킹하는 것을 차단하는 것 같다.
그런 보안이 없는 환경에서.. 믿을 만한 PC가 아니라면 중요한 정보는 입력하지 않는게 좋겠다.
후킹 과정 요약.
user32.dll의 SetWindowsHookEx로 후킹
SetWindowsHookEx로 후킹할 때 필요한 콜백함수에 대한 대리자 선언(keyboardHookProc)
후킹한 키 값을 저장할 구조체 선언(keyboardHookStruct)
후킹할 키 값을 List
키보드에 대한 이벤트 추가(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
///
/// 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
///
public globalKeyboardHook() {
hook(); // 후킹을 시작할 함수
}
///
/// Releases unmanaged resources and performs other cleanup operations before the
///
///
~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);
}
///
///
///
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
///
[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
///
[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
///
[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;
}