Reversing/Game Hack

win32API - 기초

게임의 내부 메모리 구조를 리버싱을 통해 분석하고, win32API로 오버레이를 만들어 ESP를 만들어볼거다. 

원래 해놨던 작업들을 한꺼번에 블로그 포스팅에 정리해봤는데.. 결과가 신기하긴 한데 괜히 했다 싶기도 한 그런 경험이었다 ㅋㅋ

 

#include <windows.h>
/* 윈도우 프로시저 선언 */
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

HWND : GUI요소를 위해 사용되는 인자. 어떤 윈도우에서 콜백이 발생하는지를 지정

UINT : Unsigned Int . 어떤 종류의 메시지인지 메지시 아이디를 인자로 넘김

WPARAM, LPARAM : 윈도우가 메시지에 자료를 추가할 떄 사용하는 매개변수

  • wparam : word로 넘기는 데이터 . 주로 핸들이나 정수를 전달할 때 사용
  • lparam : long 으로 넘기는 데이터. 주로 포인터 값을 전달할 때 사용
#define IDBUTTON 102

/* 클래스 이름을 전역 변수로 생성 */
TCHAR szClassName[] = TEXT("MyFirstProgram");
HINSTANCE g_hInst;

IDBUTTON : GUI 컨트롤 ID를 위한 기본 정의로, 버튼이 클릭되었을 떄 실행할 일을 연결할 떄 사용함.

szCalssName : 윈도우 운영체제 내에서 프로그램 등록에 필요한 클래스의 이름임.

HINSTANCE : 프로그램의 인스턴스. GUI가 어떤 프로그램에 소속되어있는지를 정의하기 위해 사용됨.

int WINAPI WinMain (HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine,
                    int nCmdShow)
{
  HWND hWnd;                    /* 윈도우를 위한 처리기 */
  MSG msg;                      /* 프로그램에 전송된 메시지가 저장 */
  WNDCLASSEX wcex;              /* windowclass를 위한 자료 구조체 */

WinMain은 일반 C 프로그램의 main 함수와 같은 동작을 함. 프로그램 시작시에 실행될 함수임.

  • HINSTANCE hInstance : 리소스에 접근할 수 있는 핸들 인스턴스.
  • HINSTANCE hPrevInstance : 자신이랑 동일한 프로그램이 실행중이라면 그 값을 넘겨받는 핸들
  • LPSTR lpCmdLine : 인터페이스 생성후 사용시에 전에 테스트할때 사용되는 됨.
  • int nCmdShow : 실행용 인자

HWND는 프로그램의 메인 윈도우가 될 처리기이고, 메시지 처리를 담당할 MSG, 프로그램 정보를 등록할 때 쓰이는 구조체인 WNDCLASSEX.

  • 컴파일시 오류 C28251 : winmain에 대한 주석이 일치하지 않습니다 → 프로젝트 속성 - 링커- 시스템-하위시스템 을 콘솔에서 창으로 변경
//윈도우 구조체
    g_hInst = hInstance;
    wc.hInstance = hInstance;
    wc.lpszClassName = szClassName; //클래스 이름 
    wc.lpszMenuName = NULL; //프로그램의 메뉴 사용 안함
    wc.lpfnWndProc = WndProc; //호출하는 주체가 윈도우 
    wc.style = CS_DBLCLKS; //동작의 특성 지정. 더블클릭 잡아냄
    wc.cbSize = sizeof(WNDCLASSEX);

    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); //아이콘 핸들. 아이콘 지정 
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); //작은아이콘 핸들. 작게 보일 아이콘 지정
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); //커서 핸들. 기본 마우스 커서 사용 
    wc.cbClsExtra = NULL; //윈도우 클래스 뒤에 여유 바이트 없음 
    wc.cbWndExtra = NULL; //윈도우 인스턴스 또는 구조체 

    //창 배경으로 윈도우 기본 색상 사용 
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

    //wc를 윈도우 클래스로 등록, 실패 시 프로그램 종료
    if (!RegisterClassEx(&wc))
        return 0;
HWND hWnd = CreateWindow(szClassName, L"world",
        WS_OVERLAPPEDWINDOW, 100, 90, 400, 350, NULL, NULL, hInstance, NULL);
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
  • 창을 생성. 인자는 순서대로 윈도우의 이름, 위에 들어갈 타이틀명, 윈도우 스타일, 프로그램이 화면에 표시될 위치 좌표 인자 둘, 창 너비, 창 넓이, 부모창, 메뉴, 프로그램 인스턴스, 생성 자료 이다.
  • 이때 윈도우의 이름은 wc.plszClassName과 동일하게 지정해주어야 한다.
  • showWindow는 창을 어떤 상태로 보여줄 것인지를 지정한다.
  • UpdateWindow는 시스템 리소스가 부족해지더라도 프로그램의 갱신을 중단하지 않을것을 정의한다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HWND hWndButton;
    switch (message) {
    case WM_COMMAND: {
        if ((HWND)lParam && ((HIWORD(wParam) == BN_CLICKED))) {
            int iMid;
            iMid = LOWORD(wParam);
            switch (iMid) {
            case IDBUTTON: {
                MessageBox(hWnd, TEXT("CLICK !"), TEXT("my program"), MB_OK | MB_ICONEXCLAMATION);
                break;
            }
            default:
                break;
            }

        }
        break;
    }
    case WM_CREATE: {
        hWndButton = CreateWindowEx(0,  /* 더 또는  '확장된' 모양 */
            TEXT("BUTTON"),             /* 만들고픈 GUI '클래스' */
            TEXT("날 눌러줘!"),          /* GUI 자막(Caption) */
            WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, /* 컨트롤 모양은 |로 분할 */
            10,                         /* 좌측 위치 (좌측으로부터의 위치) */
            10,                         /* 상단 위치 (상단으로부터의 위치) */
            200,                        /* 컨트롤의 너비 */
            30,                         /* 컨트롤의 높이 */
            hWnd,                       /* 부모 창 처리 */
            (HMENU)IDBUTTON,            /* WM_COMMAND를 위한 컨트롤의 ID */
            g_hInst,                    /* 응용프로그램 인스턴스 */
            NULL);
        break;
    }
    case WM_DESTROY: {
        MessageBox(hWnd, TEXT("destroy! "), TEXT("my program"), MB_OK | MB_ICONEXCLAMATION);
        PostQuitMessage(0);
        break;
    }
    case WM_CLOSE: {
        MessageBox(hWnd, TEXT("close!"), TEXT("my program"), MB_OK | MB_ICONEXCLAMATION);
        PostQuitMessage(0);
        break;
    }
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
}

몇가지만 짚어보면, switch - case문으로 구분하여 message의 종류에 따라 다른 동작을 수행하게 끔 구분할 수 있다.

  • DefWindowProc을 사용하면 처리되지 않은 메시지들을 다 몰아서 처리하게끔 해준다.
  • WM_DESTROY와 WM_CLOSE 모두 프로그램의 x창을 눌렀을때에 호출되는 이벤트로써, 서로 메시지가 전달되는 시점이 다르다.
    • CLOSE의 경우 창이 닫히기 전에 메시지가 전달되며, 대표적인 예시로 이 이벤트에 대응하여 창을 닫을 것 인지 물어보는 창을 띄울 수 있다. 메시지 박스가 뜬다.
    • DESTROY는 창이 닫힐 때 메시지가 전달되며, 프로그램을 종료한 다음에 해야하는 할당 해제, 파일 삭제 등의 동작을 주로 실행한다. 메시지 박스가 뜨지 않음.
  • WM_QUIT은 postQuitMessage 함수 호출에 의해 발생하며 GetMessage함수가 0을 리턴하게 하여 메시지 루프를 종료시키는 역할를 한다. 즉 PostQuitMessage(0)을 실행하면 WM_QUIT이 전달되어 GetMessage 루프를 종료한다는 뜻.
  • WM_CREATE는 새로운 윈도우가 생성될 때 전달되는 메시지로, 윈도우가 최초 생성 될 떄 한번만 전달되는 메시지이다. 즉, 이 이벤트에 대응하여 동작을 만드는 건 창이 최초로 뜰때 이 동작을 실행하라는 의미. 프로그램의 기본적인 초기화 작업을 수행 하기에 알맞다.

실행시

클릭하면

x로 닫으면