2021. 12. 8. 01:04ㆍ보안 연구/Reversing
본 티스토리 블로그는 PC환경에 최적화되어 있습니다.
모바일 유저분들은 아래 티스토리 블로그를 이용해 주세요.
안녕하세요! ICMP입니다!
제가 윈도우 리버싱을 공부하면서 가장 난항을 겪었던 부분이 바로 훅이 아닐까 생각합니다.
개념 자체는 매우 쉽지만 막상 스스로 구현하려고 하니 기존 소스코드가 visual studio 버전과 호환되지 않아 많은 난항을 겪었습니다.
이번 visual studio 2022 버전에 정상적인 동작을 진행할 수 있도록 간단한 키보드 후킹 코드를 작성해보았으니 학습에 참고 바랍니다.
- 본 소스코드는 리버싱 핵심원리의 메시지 훅 일부 코드를 수정하였음을 알립니다.
1. 구현 환경 : visual studio 2022
2021.12.08 일자 기준 최신 비주얼 스튜디오 버전으로 정상 작동이 됨을 확인하였습니다.
2. 구현 기능 : 메모장 키보드 훅
간단히, 메모장을 구동하여 입력값이 'q'가 입력되기 전까지 입력되지 않도록 하고, 만약 입력값이 'q'이면 훅을 해제하는 코드를 작성해 보도록 하겠습니다.
코드 설계)
- hooktest.cpp
messagehook.dll 정보를 불러와 키보드 훅을 설치, 입력값을 체크한 후 훅을 해제하는 함수를 호출하는 코드입니다.
#include <stdio.h>
#include <conio.h>
#include <windows.h>
int main() {
void(*hookstart)();
void(*hookstop)();
HMODULE dll = LoadLibraryA("./messagehook.dll"); //dll 정보를 가져옴
if (dll == NULL) {
printf("DLL파일이 정상적으로 로딩되지 않았습니다!");
return 0;
}
hookstart = (void (*)())GetProcAddress(dll, "HookStart");
hookstop = (void (*)())GetProcAddress(dll, "HookStop");
printf("키보드 훅을 설치합니다!\n");
hookstart();
while (1) {
if (_getch() == 'q')
break;
};
printf("훅을 해제합니다.\n");
hookstop();
return 0;
}
- messagehook.dll
키보드 훅을 설치하고 해제해주는 함수들의 정의를 선언해놓은 라이브러리입니다.
크게 HookStart, HookStop 함수를 정의해놓았습니다.
#include "pch.h"
#include <stdio.h>
#include <windows.h>
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInstance = hModule;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
char szPath[MAX_PATH] = { 0, };
char* p = NULL;
if (nCode >= 0)
{
// bit 31 : 0 => press, 1 => release
if (!(lParam & 0x80000000))
{
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');
// 현재 프로세스 이름을 비교해서 만약 notepad.exe 라면 0 아닌 값을 리턴함
// => 0 아닌 값을 리턴하면 메시지는 다음으로 전달되지 않음
if (!_stricmp(p + 1, "notepad.exe"))
return 1;
}
}
// 일반적인 경우에는 CallNextHookEx() 를 호출하여
// 응용프로그램 (혹은 다음 훅) 으로 메시지를 전달함
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void HookStart()
{
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
}
__declspec(dllexport) void HookStop()
{
if (g_hHook)
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
#ifdef __cplusplus
}
#endif
visual studio 2022 버전 업이 되면서 dll 로딩 및 함수 포인터 활용은 동일하지만, dll 파일을 만드는 부분의 설정 일부가 많이 바뀌었지만, 이 부분들은 건드리지 말고 그냥 작성해주시면 충분히 해낼 수 있습니다.
추가적으로 아래 파일을 첨부하니 visual studio 2022로 열어서 구조를 확인하시면 보다 이해하기 수월할 것입니다.
위 경로에 소스코드를 정확히 입력하고 컴파일하면 메모장에 정상적으로 키보드 훅이 걸림을 알 수 있으며, 종료하고 싶으시면 'q' 자판을 눌러 훅을 해제하시면 됩니다.
작성, 실행하면서 발견된 문제점이 메모장이 아닌 다른 프로세스 일부에 훅이 걸리는 현상이 확인되었으며 일부 백신 프로그램이 악성코드로 분류하여 탐지하므로 실행 시 참고 바랍니다.
이상! ICMP 였습니다. 감사합니다!
+ 2022.09.06 내용 추가)
실습을 위해 파일을 컴파일한 후 hooktest.exe를 실행하면 아래와 같은 에러가 발생하며 훅이 걸리지 않고 프로그램이 죽어버리는 경우가 존재합니다.
에러가 발생하는 원인은 많기 때문에 원인 해결을 위해서는 크게 두 가지 방법이 존재합니다.
- GetLastError()
윈도우는 개발자들의 디버깅 정보제공을 위해 에러코드를 반환해주는 함수 기능을 제공하며, 해당 함수의 반환 값을 확인하면 오류 원인을 확인할 수 있습니다.
- 프로그램이 정상적으로 DLL 파일 경로를 찾지 못해 오류가 뜨는 경우
1) DLL파일을 release 모드로 설정한 뒤 빌드를 진행합니다.
2) 생성된 .dll 파일을 hooktest.exe가 존재하는 경로로 이동
그러면 아래와 같이 정상적으로 DLL 파일이 로딩되면서 훅이 걸린 모습을 확인할 수 있습니다.
'보안 연구 > Reversing' 카테고리의 다른 글
프로젝트 2일차 (0) | 2022.01.15 |
---|---|
프로젝트 1일차 (0) | 2022.01.15 |
Hardware Reversing - 아두이노 기본(1) (0) | 2022.01.03 |
악성코드 분석 일지(한글문서) (0) | 2021.09.21 |
악성코드 분석 일지(랜섬웨어) (0) | 2021.09.19 |