[Reversing] patch

2024. 8. 9. 00:33·Hacking/Write Up
728x90
반응형

문제 : https://dreamhack.io/wargame/challenges/49/

 

patch

flag를 그리는 루틴을 분석하고 가려진 flag를 보이게 해주세요. Reference GDI+ - Win32 apps | Microsoft Docs Graphics Functions - Win32 apps | Microsoft Docs File — x64dbg documentation

dreamhack.io

Patch.exe 실행

플래그에 현재 선을 긋는 코드가 추가되어 있는 것으로 추정됨

Static Analysis

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
  HCURSOR CursorW; // rax
  HWND Window; // rax
  HWND v8; // rbx
  HACCEL AcceleratorsW; // rbx
  WNDCLASSEXW v11; // [rsp+60h] [rbp-A8h] BYREF
  struct tagMSG Msg; // [rsp+B0h] [rbp-58h] BYREF

  LoadStringW(hInstance, 0x67u, &WindowName, 100);
  LoadStringW(hInstance, 0x6Du, &ClassName, 100);
  v11.cbSize = 80;
  v11.style = 3;
  v11.lpfnWndProc = (WNDPROC)sub_1400032F0;
  *(_QWORD *)&v11.cbClsExtra = 0i64;
  v11.hInstance = hInstance;
  v11.hIcon = LoadIconW(hInstance, (LPCWSTR)0x6B);
  CursorW = LoadCursorW(0i64, (LPCWSTR)0x7F00);
  *(__m128i *)&v11.hbrBackground = _mm_load_si128((const __m128i *)&xmmword_1400053F0);
  v11.hCursor = CursorW;
  v11.lpszClassName = &ClassName;
  v11.hIconSm = LoadIconW(hInstance, (LPCWSTR)0x6C);
  RegisterClassExW(&v11);
  qword_140007880 = (__int64)&unk_1400078A0;
  Window = CreateWindowExW(0, &ClassName, &WindowName, 0xC80000u, 0x80000000, 0, 600, 200, 0i64, 0i64, hInstance, 0i64);
  v8 = Window;
  if ( Window )
  {
    hWnd = Window;
    dword_140007920 = 600;
    dword_140007924 = 200;
    GdiplusStartup(&unk_1400078A0, &dword_1400078A8, 0i64);
    ShowWindow(v8, nShowCmd);
    UpdateWindow(v8);
    AcceleratorsW = LoadAcceleratorsW(hInstance, (LPCWSTR)0x6D);
    while ( GetMessageW(&Msg, 0i64, 0, 0) )
    {
      if ( !TranslateAcceleratorW(Msg.hwnd, AcceleratorsW, &Msg) )
      {
        TranslateMessage(&Msg);
        DispatchMessageW(&Msg);
      }
    }
    LODWORD(Window) = Msg.wParam;
  }
  return (int)Window;
}

 

WinMain 함수에서 시작함 

해당 부분을 보면, v11을 변수로 RegisterClassExW함수를 사용하는 것을 볼 수 있음

RegisterClassExW함수는 Win32API 함수로 윈도우 클래스를 등록하며, 인자로는 v11을 활용하므로 해당 부분에서 콜백 함수를 찾아야함

더보기

[ v11 구조체 ]
typedef struct tagWNDCLASSEXA {
  UINT      cbSize;
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCSTR    lpszMenuName;
  LPCSTR    lpszClassName;
  HICON     hIconSm;
} WNDCLASSEXA, *PWNDCLASSEXA, *NPWNDCLASSEXA, *LPWNDCLASSEXA;

 

https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexa

v11 구조체의

WNDPROC lpfnWndProc;
부분은 윈도우 메세지의 콜백을 처리하는 부분으로 이 함수가 어떤 역할을 하는지에 대해 알아야한다.

즉, 윈도우 창을 띄우고 어떤 역할을 하는지는 해당 콜백함수가 결정하는 것

 

sub_1400032F0가 콜백함수이므로 살펴보도록한다.

LRESULT __fastcall sub_1400032F0(HWND a1, UINT a2, WPARAM a3, LPARAM a4)
{
  _QWORD *v5; // rbx
  __int64 v6; // rbx
  __int64 v7; // [rsp+20h] [rbp-18h] BYREF

  switch ( a2 )
  {
    case 2u:
      PostQuitMessage(0);
      return 0i64;
    case 0xFu:
      qword_140007910 = (__int64)BeginPaint(hWnd, &Paint);
      v5 = (_QWORD *)GdipAlloc(16i64);
      if ( v5 )
      {
        *v5 = 0i64;
        v5[1] = 0i64;
        v7 = 0i64;
        *((_DWORD *)v5 + 2) = GdipCreateFromHDC(qword_140007910, &v7);
        *v5 = v7;
      }
      else
      {
        v5 = 0i64;
      }
      qword_140007918 = (__int64)v5;
      sub_140002C40();
      v6 = qword_140007918;
      if ( qword_140007918 )
      {
        GdipDeleteGraphics(*(_QWORD *)qword_140007918);
        GdipFree(v6);
      }
      EndPaint(hWnd, &Paint);
      return 0i64;
    case 0x202u:
      InvalidateRect(hWnd, 0i64, 1);
      UpdateWindow(hWnd);
      return 0i64;
    default:
      return DefWindowProcW(a1, a2, a3, a4);
  }
}

Begin Paint와 EndPaint 사이에 현재 플래그를 출력하는 부분이 존재할 것이므로, 해당 부분 사이를 살펴본다.

Switch 문에서 0xF(unsigned) case임을 확인할 수 있음

현재 이 코드에서 그림을 그린다고 할 수 있는 부분을 확인하지 못했기 때문에 sub_140002C40()함수를 확인한다.

 

char __fastcall sub_140002C40(__int64 a1, int a2)
{
  int v2; // ebx
  int v3; // edx
  int v4; // edx
  int v5; // edx
  int v6; // edx
  int v7; // edx
  int v8; // edx
  int v9; // edx
  int v10; // edx
  int v11; // edx
  int v12; // edx
  int v13; // edx
  int v14; // edx
  int v15; // edx
  int v16; // edx
  int v17; // edx
  int v18; // edx
  int v19; // edx
  int v20; // edx
  int v21; // edx
  int v22; // edx
  int v23; // edx
  int v24; // edx
  int v25; // edx
  int v26; // edx
  __int64 v27; // rbx
  __int64 v28; // r8
  __int64 v29; // r8
  __int64 v30; // rdx
  __int64 v31; // r8
  __int64 v32; // rdx
  __int64 v33; // r8
  __int64 v34; // rdx
  __int64 v35; // r8
  __int64 v36; // rdx
  __int64 v37; // r8
  __int64 v38; // rdx
  __int64 v39; // r8
  __int64 v40; // rdx
  __int64 v41; // r8
  __int64 v42; // r8
  __int64 v43; // rdx
  __int64 v44; // r8
  __int64 v45; // r8
  __int64 v46; // rdx
  __int64 v47; // r8

  v2 = qword_140007880;
  sub_140002B80(qword_140007880, a2, 30, 470, 80, -16777216);
  sub_140002B80(v2, v3, 35, 470, 75, -16777216);
  sub_140002B80(v2, v4, 40, 470, 70, -16777216);
  sub_140002B80(v2, v5, 45, 470, 65, -16777216);
  sub_140002B80(v2, v6, 50, 470, 60, -16777216);
  sub_140002B80(v2, v7, 55, 470, 55, -16777216);
  sub_140002B80(v2, v8, 60, 470, 50, -16777216);
  sub_140002B80(v2, v9, 65, 470, 45, -16777216);
  sub_140002B80(v2, v10, 70, 470, 40, -16777216);
  sub_140002B80(v2, v11, 75, 470, 75, -16777216);
  sub_140002B80(v2, v12, 80, 400, 60, -16777216);
  sub_140002B80(v2, v13, 30, 470, 90, -16777216);
  sub_140002B80(v2, v14, 35, 470, 30, -16777216);
  sub_140002B80(v2, v15, 40, 470, 35, -16777216);
  sub_140002B80(v2, v16, 45, 470, 50, -16777216);
  sub_140002B80(v2, v17, 50, 470, 40, -16777216);
  sub_140002B80(v2, v18, 55, 400, 90, -16777216);
  sub_140002B80(v2, v19, 60, 470, 60, -16777216);
  sub_140002B80(v2, v20, 65, 470, 30, -16777216);
  sub_140002B80(v2, v21, 70, 470, 80, -16777216);
  sub_140002B80(v2, v22, 75, 470, 70, -16777216);
  sub_140002B80(v2, v23, 80, 470, 60, -16777216);
  sub_140002B80(v2, v24, 80, 470, 80, -16777216);
  sub_140002B80(v2, v25, 80, 470, 70, -16777216);
  sub_140002B80(v2, v26, 90, 470, 90, -16777216);
  v27 = qword_140007880;
  sub_1400017A0(qword_140007880, 40i64, v28, 4278190080i64);
  sub_140001C80(v27, 80i64, v29, 4278190080i64);
  sub_140002640(v27, v30, v31, 4278190080i64);
  sub_1400020F0(v27, v32, v33, 4278190080i64);
  sub_140002390(v27, v34, v35, 4278190080i64);
  sub_140001240(v27, v36, v37, 4278190080i64);
  sub_140001F20(v27, v38, v39, 4278190080i64);
  sub_140001560(v27, v40, v41, 4278190080i64);
  sub_140001C80(v27, 360i64, v42, 4278190080i64);
  sub_1400019D0(v27, v43, v44, 4278190080i64);
  sub_1400017A0(v27, 440i64, v45, 4278190080i64);
  sub_140002870(v27, v46, v47, 4278190080i64);
  return 0;
}

자세한 함수를 살펴보지는 않았지만, 반복적으로 시행하는 것으로 보아 반복적으로 그림을 그리는 작업임을 유추할 수 있음

sub_140002B80 이 함수는 같은 작업을 반복하고, 이 아래에 함수들은 모두 다른 작업을 하는데, 우리가 없애야하는 부분은 선을 반복적으로 그어놓은 곳이므로 해당 함수를 살펴봐야 할 것임

 

__int64 __fastcall sub_140002B80(__int64 a1, __int64 a2, unsigned int a3, int a4, int a5, unsigned int a6)
{
  int v9; // eax
  __int64 v10; // rsi
  int v11; // eax
  __int64 v12; // rbx
  int v13; // eax
  __int64 v15; // [rsp+30h] [rbp-38h] BYREF
  __int64 v16; // [rsp+38h] [rbp-30h]

  v16 = 0i64;
  v15 = 0i64;
  v9 = GdipCreatePen1(a6, a2, 0i64, &v15);
  v10 = *(_QWORD *)(a1 + 120);
  LODWORD(v16) = v9;
  v11 = GdipDrawLineI(*(_QWORD *)v10, v15, 150i64, a3, a4, a5);
  if ( v11 )
    *(_DWORD *)(v10 + 8) = v11;
  v12 = *(_QWORD *)(a1 + 120);
  v13 = GdipSetSmoothingMode(*(_QWORD *)v12, 4i64);
  if ( v13 )
    *(_DWORD *)(v12 + 8) = v13;
  return GdipDeletePen(v15);
}

GdipCreatePen1함수를 이용해 펜을 생성하고 GdipDrawLineI 함수를 사용해 선을 하나 그리는 함수

아마 저 위에 글자가 가려지는 부분은, 이 코드를 반복 적용하여 생기는 것으로 추측됨

따라서, 글자를 쓰는 부분보다 가려지는 부분이 먼저 생성된다는 것

 

Dynamic Analysis

WinDbg를 이용하여 동적분석 하기로 하였다IDA랑 WinDbg 간의 주소 베이스만 파악하여 그림이 그려지기 전에 bp를 걸었다.

 

해당 부분이 선을 그리는 부분임을 확인할 수 있음

 

Binary Patch

선을 그리는 sub_140002B80함수가 아무런 일도 하지 않고 바로 return 하도록 패치하여 플래그를 얻을 수 있게 하고자 함

IDA에서 Edit → Patch Program → Assemble을 통해 패치를 진행한다.

맨 처음에 있는 어셈블리를 바로 ret로 변경해줌

이를 apply 시켜준 후 exe 파일을 실행시키면, 해당 함수가 실행되지 않은 파일을 얻을 수 있음

 

피드백

정석적인 풀이방법이였다

패치하지 않고도 풀 수 있었는데, 그냥 저 함수를 실행하는 부분을 아예 건너 뛰어서 할 수 있었다.

rip를 아예 선 그리는 부분에서 옮겨주거나, 저 함수만 건너뛰어서 실행시키면 됨

 

생각보다 IDA에서 생성해주는 함수 이름이 정확하니 함수 이름을 보고 추론해서 분석하는 것도 중요한 능력

 

 

Reference >

https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassexw

 

RegisterClassExW function (winuser.h) - Win32 apps

Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function. (RegisterClassExW)

learn.microsoft.com

 

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'Hacking > Write Up' 카테고리의 다른 글

[Pwnable] Return to Library  (2) 2024.09.16
[Pwnable] Return to Shellcode  (4) 2024.08.24
[Cryptography] Textbook-CBC  (4) 2024.08.03
[Reversing] Reversing Basic Challenge #1  (0) 2024.07.28
[Reversing] Reversing Basic Challenge #0  (3) 2024.07.28
'Hacking/Write Up' 카테고리의 다른 글
  • [Pwnable] Return to Library
  • [Pwnable] Return to Shellcode
  • [Cryptography] Textbook-CBC
  • [Reversing] Reversing Basic Challenge #1
min_zu
min_zu
  • min_zu
    민주제도
    min_zu
  • 전체
    오늘
    어제
    • ._. (176)
      • AI (2)
        • DeepLearning (2)
        • CS231n (0)
      • Web (2)
        • ReactJS (0)
      • CS (83)
        • OS (7)
        • Data Structure (23)
        • Computer Architecture (8)
        • Computer Network (20)
        • Algorithm (25)
      • Linux (3)
        • KaliLinux (0)
        • Docker (1)
      • Hacking (83)
        • Write Up (25)
        • Pwnable (13)
        • Reversing (2)
        • Cryptography (12)
        • Web Hacking (4)
        • Window (6)
        • Network (7)
        • Web3 (13)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    ComputerArchitecture
    Web
    WinAFL
    OS
    DataStructure
    Mac
    DeepLearning
    Search
    Tree
    Linux
    Graph
    AI
    Sort
    UTM
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
min_zu
[Reversing] patch
상단으로

티스토리툴바