서론
이번 코스에서는 드림핵 워게임 중 patch문제를 함께 풀어보면서 이제껏 학습한 지식과 도구를 응용해 본다.
patch 문제는 아래의 링크에서 확인할 수 있다.
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 파일 실행
- Patch 정적 분석
- Patch 동적 분석
- Patch 파일 바이너리 수정
파일 실행
다운로드 받은 파일을 실행해 프로그램이 어떤식으로 동작하는지 확인해 보았다.
워게임 문제와 같이 신뢰할 수 없는 프로그램을 실행할 때는 가상환경(VM)에서 실행하는 것이 바람직하다.
만약 문제 정보를 통해 프로그램의 동작방식이 예상이 간다면 굳이 실행하지 않고 정적분석을 먼저 시도해보는 것도 좋은 방법이다.

이 프로그램은 GUI 프로그램이기 때문에 프로그램을 바로 더블클릭 해 실행할 수 있다.
DH{}로 감싸진 글자가 출력되는 것으로 보아 출력되는 문자열이 플래그임을 알 수 있다.
하지만 중간에 플래그를 가리는 내용의 마스킹 때문에 플래그의 내용이 무엇인지는 확인할 수 없다.
따라서 문제 프로그램을 직접 패치해 중간에 선을 그리는 부분을 찾고 이 부분을 제거해 플래그가 가려지지 않도록 하는 것이 목적임을 알 수 있다.
정적분석과 동적분석을 통해 선을 그리는 부분을 찾고 패치하면 문제를 해결할 수 있다.
IDA로 파일 열기
문제 파일을 정적분석하기 위해 앞서 배운 IDA를 사용했다.
IDA를 실행하고, Patch.exe를 끌어넣는다.
파일이 정상적으로 열리면 다음과 같은 창이 나타나는데, 아무것도 변경하지 말고, OK를 클릭하면 된다.

WinMain 함수 찾기
Patch 문제는 WinAPI를 이용해 만들어진 GUI 프로그램이기 때문에 WinMain 함수를 찾아야 한다.
WinMain 함수에서는 보편적으로 윈도우를 생성하고 관련된 초기화 작업을 진행한다.
따라서 이와 관련된 API를 상호참조하는 방식으로 찾을 수 있다.
IDA가 WinMain 함수를 자동으로 찾아주지만 함수 상호 참조를 통해 직접 찾아보았다.
프로그램이 DLL로부터 임포트해 사용하는 함수의 목록은 기본 아이다 화면에서 Imports 탭에 존재한다.
이 중 보편적으로 윈도우를 생성할 때 사용하는 CreateWindowExW 함수 이름을 더블클릭해 함수가 임포트되는 곳으로 갈 수 있다.
단축키 x를 이용해 CreateWindowExW 함수가 사용되는 곳을 보면 WinMain 함수에서 사용하고 있음을 확인할 수 있다.

WinMain 함수 분석

F5 단축키를 이용해 디컴파일한 WinMain 함수를 볼 수 있다.
이 중 RegisterClassExW 함수가 윈도우 클래스를 등록하는 함수인데 인자로 v11 변수가 들어감을 확인할 수 있다.
여기서 가장 중요한 부분은 윈도우의 메시지 콜백을 설정하는 부분인 14 라인이다.
보편적으로 메시지 콜백함수에서 윈도우의 동작이 정의되어 있기 때문에 sub_1400032F0 함수를 분석해 어떤 동작을 수행하는지 알아야 한다.
WinAPI를 사용해 GUI 프로그램을 구성하는 방법에 대해 익숙하지 않다면 관련된 함수를 직접 검색해서 용도를 파악하는 것이 낫다.
WinAPI 함수의 문서는 MSDN에서 검색하면 함수의 용도부터 각 인자의 설명까지 나오기 때문에 모르는 함수가 나왔을 때에는 검색하는 습관을 들이는 것이 좋다.
WinAPI를 이용한 프로그램은 대체적으로 사용하는 API가 비슷하기 때문에 모르는 함수가 나올 때마다 알아둔다면, 나중에 다른 바이너리를 분석할 때 더 효율적으로 분석할 수 있다.
메세지 콜백 함수 분석

위의 페이지에서 디컴파일된 sub_1400032F0 함수를 볼 수 있다.
switch case 구문 내에 총 세 개의 case가 존재하는데 이 중 0xF에 해당하는 case에서 Paint와 관련된 역할을 한다.
BeginPaint 함수와 EndPaint 함수 사이에서 플래그를 출력해 줄 것이기 때문에 가운데 있는 sub_140002C40 함수를 분석하면 어떻게 플래그를 그리는지 볼 수 있을 것이다.

위의 이미지는 디컴파일된 sub_140002C40 함수이다.
51 라인부터 75 라인까지 모두 sub_140002B80 함수를 호출하고, 나머지 각각의 다른 함수들이 아래에서 호출된다.
아직 함수의 내용을 분석하진 않았지만 각 함수의 역할을 추측해보면 다음과 같이 추측할 수 있다.
sub_140002B80 함수는 같은 함수가 연속적으로 호출되는 것으로 보아 무언가 반복적인 형태를 그리는 것으로 추측할 수 있고, 이후 각각 다른 주소의 함수들은 다른 형태의 그림을 하나씩 그리는 것으로 추측할 수 있다.
좀 더 명확한 분석을 위해 sub_140002B80 함수를 분석해보았다.

위의 이미지를 보면 GdipCreatePen1함수를 이용해 펜을 생성하고 GdipDrawLineI 함수를 사용해 선을 하나 그리는 것을 볼 수 있다.
이번엔 함수 내용 비교를 위해 sub_140002B80 이후에 호출되는 함수인 sub_1400017A0 함수를 분석해 보았다.

네 번째 이미지 페이지에서 sub_1400017A0는 sub_140002B80 함수와 다르게 펜을 생성한 후, 여러개의 선을 그리는 것을 볼 수 있다.
이를 통해 이 부분이 글자를 하나씩 그리는 부분이라고 추측할 수 있다.
추측한 내용이 맞는지 검증하기 위해 이번엔 직접 디버깅을 통해 분석해 보았다.
메세지 콜백 함수 디버깅
sub_140002B80 함수를 호출하면 어떤 일이 발생하는지 직접 확인하기 위해 브레이크 포인트를 걸고 디버깅을 해보았다.

메시지 콜백 함수인 sub_1400032F0에서 첫 번째 sub_140002B80 함수를 호출하는 부분에 F2 단축키를 이용해 브레이크 포인트를 건 후, F9 단축키를 눌러 프로그램을 실행했다.
브레이크 포인트가 걸리면 두 번째 이미지 페이지와 같이 표시된다.

여기서 함수의 주소와 이름이 변경된 이유는 프로그램이 실행되면서 base 주소가 변경되었기 때문이다.
함수의 내용은 변하지 않으니 무시하고 분석하면 된다.
F8 (Step Over) 단축키을 눌러 함수를 실행한 후 프로그램에 어떤 변화가 생기는지 알아보았다.
디버깅 중일 때에는 프로그램의 모든 동작이 멈춰있는 상태이기 때문에 프로그램 윈도우 창을 띄울 수 없다.
따라서 Alt+Tab을 누른 상태로 윈도우에 어떤 변화가 생겼는지 확인하는 방식으로 프로그램의 변화를 확인해야 한다.

이미지 페이지를 보면 선 하나가 그려진 것을 볼 수 있다.
F8 (Step Over)를 한 번 더 누르면 또 다른 선이 그려지는 것을 보아 sub_140002B80 함수가 플래그를 가리는 선을 그리는 함수로 확신할 수 있다.
이제 선을 그리는 함수가 실행되지 않도록 바이너리를 직접 패치하면 된다.
함수 패치
함수를 패치하는 방법은 굉장히 다양하다.

함수 패치에 대한 기능이 모여있는 Edit - Patch Program 메뉴 중 어셈블리 인스트럭션을 패치하기 위해 Assemble 기능을 사용했다.
선을 그리는 함수가 sub_140002B80 함수였기 때문에 g 단축키를 이용해 해당 함수로 이동해준다.
이 후 어셈블리 화면에서 첫 번째 인스트럭션에 커서를 올리고 Edit - Patch Program - Assemble... 메뉴를 클릭하면 인스트럭션을 입력할 수 있는 창이 뜬다.

이 함수가 선을 그리지 않고 그대로 반환하도록 패치해야 하기 때문에 인스트럭션 입력 칸에 함수를 바로 리턴하도록 ret 인스트럭션을 입력하고 OK를 눌러준다.

OK를 누르면 함수의 형태가 깨지면서 인스트럭션이 retn으로 변경된 것을 확인할 수 있다.
IDA가 기존에 정의해 둔 함수의 형태에서 인스트럭션이 변경되었기 때문에 UI에 변화가 생겼지만 크게 신경쓰지 않아도 된다.

이제 패치한 내용을 실제 바이너리에 적용하기 위해 Edit - Patch Program - Apply patches to input file... 메뉴를 클릭해준다.
적용 창에서 아무것도 변경하지 않고 OK를 누르면 바이너리 패치가 실제 파일에 적용된다.
플래그 인증
바이너리 패치가 적용된 파일을 다시 실행하면 선이 제거되고, 실제 플래그를 확인할 수 있다.

이미지를 확인해보면, 패치한대로 선을 그리지 않고 전체 플래그가 출력되는 것을 확인할 수 있다.

마치며
다음은 IDA의 단축키 목록이다.
- 함수/주소 이동 (g): 입력한 함수, 주소로 이동
- 상호참조 (x): 함수, 변수를 사용하는 곳을 찾음
- 브레이크 포인트 (F2): 디버깅 시 중단점을 설정
- Step Over (F8): 디버깅 시 인스트럭션 하나를 실행함, 이 때 함수를 만날 시 해당 함수는 바로 실행
- 실행 (F9): 바이너리를 실행합니다. 이 때 실행 도중 중단점을 만나면 멈춤
- Edit - Patch Program: 바이너리 패치와 관련된 메뉴가 존재함