[C#] 키보드와 마우스의 전역 이벤트를 사용하는 법(키보드와 마우스 후킹)


Development note/C#  2021. 1. 20. 17:06

안녕하세요. 명월입니다.


이 글은 C#의 키보드와 마우스의 전역 이벤트를 사용하는 법(키보드와 마우스 후킹)에 대한 글입니다.


우리가 C#으로 윈도우 폼을 작성을 해서 여러가지 액션 기능을 넣기 위해서는 이벤트를 사용합니다.

예를 들면 버튼의 경우라면 클릭 이벤트라던가, 여러가지 마우스 또는 키 이벤트를 사용합니다.

using System;
using System.Windows.Forms;

namespace Example
{
  // Form 클래스를 상속받는다.
  class Program : Form
  {
    // 생성자
    private Program()
    {
      this.MouseMove += Program_MouseMove;
    }
    // 마우스 이동 이벤트
    private void Program_MouseMove(object sender, MouseEventArgs e)
    {
      // 콘솔에 마우스 이동 좌표를 표시한다.
      Console.WriteLine($"X = {e.X}, Y = {e.Y}");
    }
    // 싱글 스레드 어트리뷰트
    [STAThread]
    // 실행 함수
    static void Main(string[] args)
    {
      // 환경에 맞는 스타일 설정
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      // 메시지 루프에 인스턴스를 생성한다.
      Application.Run(new Program());
    }
  }
}

폼 안에서 마우스를 움직이면 콘솔에 마우스 포인터 위치가 출력되는 것을 확인할 수 있습니다. 그런데 만약 이 윈도우 폼을 벗어나면 당연히 마우스 포인터는 찍히지 않습니다.

마우스 뿐아니라 키보드 이벤트도 폼이 활성화(Active)가 되지 않는 경우, 즉 다른 프로그램이 선택되어진 경우에는 이벤트를 가져오지 않습니다.

Form이라는 것은 해당 윈도우 메시지(Window message)의 이벤트만 받기 때문에 당연한 것입니다.


그런데 우릭 프로그램을 만들다 보면 해당 프로그램을 넘어서서 이벤트를 받아와야 하는 경우도 있습니다. 그것을 전역 이벤트라고 합니다. 또는 OS의 Message를 가로챈다해서 후킹이라고도 표현합니다.

전역 이벤트를 사용하기 위해서는 Nuget에서 전역 이벤트를 사용할 이벤트를 다운로드 받아야 합니다. (기본 .NetFramework에서는 지원하지 않습니다.)

Nuget에서 hook으로 검색하면 MouseKeyHook라는 라이브러리가 나옵니다. 설치합니다.

전역 이벤트를 사용할 때는 굳이 윈도우 폼이 필요없기 때문에 윈도우 메시지(Window message)만 이용합시다.

using Gma.System.MouseKeyHook;
using System;
using System.Windows.Forms;

namespace Example
{
  // 윈도우 메시지를 사용하기 위한 ApplicationContext를 상속
  class Program : ApplicationContext
  {
    // 전역 이벤트 객체
    private IKeyboardMouseEvents globalHook;
    private Program()
    {
      // 라이브러리로부터 이벤트를 가져온다.
      globalHook = Hook.GlobalEvents();
      // 키보드 이벤트
      // 키보드 키를 누르는 이벤트
      globalHook.KeyDown += GlobalHook_KeyDown;
      // 키보드 키를 떼는 이벤트
      globalHook.KeyUp += GlobalHook_KeyUp;
      // 키보드 키를 누르고 있는 이벤트
      globalHook.KeyPress += GlobalHook_KeyPress;

      // 마우스 이벤트
      // 마우스 이동 이벤트
      globalHook.MouseMove += GlobalHook_MouseMove;
      // 마우스 이동 이벤트 (확장형) (차이를 잘 모르겠음)
      globalHook.MouseMoveExt += GlobalHook_MouseMoveExt;
      // 마우스 클릭 이벤트
      globalHook.MouseClick += GlobalHook_MouseClick;
      // 마우스 더블 클릭 이벤트
      globalHook.MouseDoubleClick += GlobalHook_MouseDoubleClick;
      // 마우스 버튼 누름 이벤트
      globalHook.MouseDown += GlobalHook_MouseDown;
      // 마우스 버튼 누름 이벤트 (확장형)
      globalHook.MouseDownExt += GlobalHook_MouseDownExt;
      // 마우스 버튼을 떼는 이벤트
      globalHook.MouseUp += GlobalHook_MouseUp;
      // 마우스 버튼을 떼는 이벤트 (확장형)
      globalHook.MouseUpExt += GlobalHook_MouseUpExt;
      // 마우스 휠 이벤트
      globalHook.MouseWheel += GlobalHook_MouseWheel;
      // 마우스 휠 이벤트 (확장형)
      globalHook.MouseWheelExt += GlobalHook_MouseWheelExt;
      // 마우스 드래그 시작 이벤트 (원쪽 버튼을 클릭한 채로 마우스를 움직이는 형태)
      globalHook.MouseDragStarted += GlobalHook_MouseDragStarted;
      // 마우스 드래그 시작 이벤트 (확장형)
      globalHook.MouseDragStartedExt += GlobalHook_MouseDragStartedExt;
      // 마우스 드래그 종료 이벤트 (드래그 중에 윈쪽 버튼을 떼는 순간)
      globalHook.MouseDragFinished += GlobalHook_MouseDragFinished;
      // 마우스 드래그 종료 이벤트 (확장형)
      globalHook.MouseDragFinishedExt += GlobalHook_MouseDragFinishedExt;
    }

    // 키보드 이벤트
    // 키보드 키를 누르는 이벤트
    private void GlobalHook_KeyUp(object sender, KeyEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Key Up " + e.KeyData);
    }
    // 키보드 키를 떼는 이벤트
    private void GlobalHook_KeyDown(object sender, KeyEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Key Down " + e.KeyData);
    }
    // 키보드 키를 누르고 있는 이벤트
    private void GlobalHook_KeyPress(object sender, KeyPressEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Key Press " + e.KeyChar);
    }

    // 마우스 이벤트
    // 마우스 이동 이벤트
    private void GlobalHook_MouseMove(object sender, MouseEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine("X = {0}, Y = {1}", e.X.ToString(), e.Y.ToString());
    }
    // 마우스 이동 이벤트 (확장형)
    private void GlobalHook_MouseMoveExt(object sender, MouseEventExtArgs e)
    {
      // 콘솔 출력
      Console.WriteLine("EXT X = {0}, Y = {1}", e.X.ToString(), e.Y.ToString());
    }
    // 마우스 클릭 이벤트
    private void GlobalHook_MouseClick(object sender, MouseEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Click {e.Clicks}");
    }
    // 마우스 더블 클릭 이벤트
    private void GlobalHook_MouseDoubleClick(object sender, MouseEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Double Click");
    }
    // 마우스 버튼 누름 이벤트
    private void GlobalHook_MouseDown(object sender, MouseEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Down");
    }
    // 마우스 버튼 누름 이벤트 (확장형)
    private void GlobalHook_MouseDownExt(object sender, MouseEventExtArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Down Ext");
    }
    // 마우스 버튼을 떼는 이벤트
    private void GlobalHook_MouseUp(object sender, MouseEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Up");
    }
    // 마우스 버튼을 떼는 이벤트 (확장형)
    private void GlobalHook_MouseUpExt(object sender, MouseEventExtArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Up Ext");
    }
    // 마우스 휠 이벤트
    private void GlobalHook_MouseWheel(object sender, MouseEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Wheel");
    }
    // 마우스 휠 이벤트 (확장형)
    private void GlobalHook_MouseWheelExt(object sender, MouseEventExtArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Wheel Ext");
    }
    // 마우스 드래그 시작 이벤트
    private void GlobalHook_MouseDragStarted(object sender, MouseEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Drag Started");
    }
    // 마우스 드래그 시작 이벤트 (확장형)
    private void GlobalHook_MouseDragStartedExt(object sender, MouseEventExtArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Drag Started Ext");
    }
    // 마우스 드래그 종료 이벤트
    private void GlobalHook_MouseDragFinished(object sender, MouseEventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Drag Finished");
    }
    // 마우스 드래그 종료 이벤트 (확장형)
    private void GlobalHook_MouseDragFinishedExt(object sender, MouseEventExtArgs e)
    {
      // 콘솔 출력
      Console.WriteLine($"Mouse Drag Finished Ext");
    }
    // 싱글 스레드 어트리뷰트
    [STAThread]
    // 실행 함수
    static void Main(string[] args)
    {
      // 메시지 루프에 인스턴스를 생성한다.
      Application.Run(new Program());
    }
  }
}

좀 정확한 차이를 두기 위해서 윈도우 폼은 생략했습니다. 윈도우 폼이 없는 데도 마우스가 움직이는 것에 따라 콘솔에 마우스 좌표가 출력됩니다.

그 외에 다른 프로그램 위에서 타이핑을 하거나 마우스를 움직여도 콘솔에 다 표시가 됩니다.


후킹이라는 것은 윈도우 메시지를 가져오는 행위로 사용자의 행위를 감시하거나 해킹을 할 수 있는 것을 만들 수 있습니다.

예를 들면, 후킹 프로그램을 특정 사용자에 몰래 설치 및 실행을 하고 마우스 클릭이나 행위를 특정 서버에 저장을 하게 한다면 데이터에 따라 아이디나 비밀 번호를 훔쳐오는 것이 가능합니다. 이것을 보통 좀비 바이러스라 이야기 합니다. 통신 패킷을 훔쳐서 분석하는 것과는 차이가 있기 때문에 해킹과는 거리가 있습니다만 결론적으로 남의 정보를 훔쳐 가져오는 것이기 때문에 불법입니다.

제가 학교 다닐 때 프로그램 공부하면서 교수님에게 가장 많이 들었던 소리가 "도둑질이라는 것은 못해서 안 하는 것이 아니라 해서는 안 되는 것을 알기 때문에 하지 않는 것이다."라고 들었습니다. 물론 해킹이라는 것을 알아야 보안이라는 분야의 공부가 가능하기 때문에, 후킹이라는 것도 알고 있어야 하지만 실제로 프로그램을 만들어 배포하는 것은 좋지 않습니다.

간혹 영화나 티비에서 해커나 크래커 등을 미화하고 그런 것을 멋있게 보는 사람도 꽤 있습니다만, 도둑질은 아무리 미화하고 멋있게 보여도 범죄자입니다. 범죄자는 감옥에 가야죠..

그러니 프로그램을 꼭 좋은 목적에 작성하셨으면 합니다.


여기까지 C#의 키보드와 마우스의 전역 이벤트를 사용하는 법(키보드와 마우스 후킹)에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.