반응형

 

Minesweeper_xp.zip
0.15MB
patched_winmine_ko.exe
0.11MB


분석 환경 : Vbox -> Windows 7 Pro 32bit

Debugger : Ollydbg

Win32 API 관련 내용 : http://soen.kr/lecture/win32api/win32lec.htm


목표


1. 지뢰가 랜덤으로 위치가 지정되어 지는 부분
2. 지뢰를 눌렀을 때 다른 지뢰들의 위치까지 보여주는 부분


주의 할 점

1. 블로깅을 하면서도 Ctrl + F2 키를 이용해 재실행을 여러번 했기 때문에 주소는 비슷하더라도 그 주소의 데이터는 다른 사진이 보일 수 있다.
2. 사진 속 주소와 실제 분석 때 주소가 다르게 보일 수 있는데 이는 사용자마다 다를 수 있는 것이므로 중요하지 않으니 이어서 따라하면 된다.


Win32 API


https://sean.tistory.com/384

Win32 API를 모른다면 위의 URL의 글을 참고하면 이 글을 이해하기 더 쉽다.


정적 분석


EntryPoint는 3E21이고, 32bit 실행 파일이라고 한다.

Address of Entry Point 3E21
Base of Code 1000
Base of Data 5000
ImageBase 1000000


PE View로 보면 위와 같다.

RVA 1000
Virtual Size 3A56
Pointer to RawData 400
Size of RawData 3C00


text 섹션 헤더의 값들을 보면 위와 같고

RVA 5000
Virtual Size 0B98
Pointer to RawData 4000
Size of RawData 200


data 섹션 헤더의 값들을 보면 위와 같다.

EP


위와 같이 Ollydbg에 올리면 1003E21 주소에서 멈추게 되는데, 1000000은 ImageBase 값이고, 3E21은 EntryPoint 값이였다.

GetModuleHandleA()


1003E36 주소의 GetModuleHandleA() API가 호출되면 EAX에는 1000000가 담기게 되는데 이 값이 현재 모듈(프로그램)의 핸들값이다.

GetModuleHandle() API는 실행 파일(exe, dll 등)의 핸들을 가져오는 함수인데

https://learn.microsoft.com/ko-kr/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlea
위의 URL에 있는 정보에 따르면, GetModuleHandle() API의 매개변수 pModule에 NULL 값을 주면 호출 프로세스(.exe 파일)를 만드는 데 사용되는 파일에 대한 핸들을 반환한다.

즉, pModule이 NULL이면 GetModuleHandleA() API를 호출한 자신(프로그램)의 핸들값을 리턴한다는 것이다.


F8 키를 눌러 쭉 트레이싱 하다 보면 위와 같이 1003F41 주소 ~ 1003F4D 주소에서 루프를 돌게 된다.

이 부분은 파일 경로 문자열(C:\Desktop\file.exe 형식)에서 문자 하나 하나 가져와 00과 비교하여 같으면 1003F4F로 점프하고
00과 같지 않으면 22와 비교하여 같으면 1003F4F 주소로 가게 되고, 22와 같지 않으면 22와 같을 때까지 반복하는 것이다.

1003F4F 주소에 F2 키를 눌러 BP를 걸고 F9 키를 눌러 실행하면 루프를 통과할 수 있다.

GetStartupInfoA()


이어서 F8 키를 누르다보면 1003F69 주소에서 GetStartupInfoA() API를 호출하게 된다.

GetStartupInfo() API는 호출 프로세스를 만들 때 지정된 STARTUPINFO 구조체의 내용을 검색한다.
(https://learn.microsoft.com/ko-kr/windows/win32/api/processthreadsapi/nf-processthreadsapi-getstartupinfow)
(https://learn.microsoft.com/ko-kr/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa)

1003F90 주소 - 10021F0 호출(WinMain)


F8 키를 눌러 진행하다 보면 1003F90 주소에서 위와 같이 10021F0 주소의 함수를 호출하는 부분을 만나는데

F7 키를 눌러 10021F0 주소의 함수 내부로 들어간다.

10021F0 주소의 함수가 WinMain()이다.


10021F0 주소의 함수 내부로 들어가면 위와 같다.

1002201 주소에서 1003AB0 주소의 함수 호출 - GetTickCount(), srand()


F8 키를 눌러 진행하다보면 1002201 주소에서 1003AB0 주소의 함수를 호출하는데 이 주소의 함수는 딱히 중요한 건 아니지만 F7 키를 눌러 한 번 들어가본다.


그러면 위와 같이 GetTickCount() 함수와 srand() 함수가 보인다.

GetTickCount() 함수는 시스템이 부팅된 후 경과된 시간(미리초)를 최대 49.7일동안 검색한다.
(https://learn.microsoft.com/ko-kr/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount)

srand() 함수는 매개변수가 1개이며, seed 값을 매개변수로 받는다.

rand() 함수에 사용될 수를 초기화 하는데 이때 인자로 받은 seed 값을 이용해 초기화한다.
(srand()와 rand()만 이용했을 때 그래도 일정한 값이 나오기 때문에 time()을 이용해 난수를 생성한다.)


1003AB4 주소에서 GetTickCount() 함수를 호출하고 나면 EAX 레지스터에 128AB4 값이 담기게 되고


1003ABA 주소의 명령이 실행되면 AX(2byte) 값을 EAX(4byte)에 옮기고, 그렇게 옮긴 8AB4 값을 스택에 넣어 srand() 함수의 인자로 준다.


1003ABE 주소의 srand() 함수가 호출되면 EAX에는 위와 같이 4007E0 값이 담기게 된다.

즉, 정리하자면 GetTickCount() 함수를 이용해 현재 시스템이 부팅된 후 얼마나 지났는지를 구해 와서 해당 값에서 2byte를 잘라 srand() 함수의 인자로 넘긴다.

그리고 F8 키를 눌러 진행하다보면 1003AB0 주소의 함수가 종료되고 위와 같이 1002206 주소로 가게 된다.

1002226 주소 - WNDCLASS 구조체


1002226 주소부터 위의 함수들의 호출을 통해 윈도우 클래스 구조체 WNDCLASS의 멤버들에 값을 채우는 것이다.

1002292 주소 - RegisterClassW()


윈도우 클래스를 생성했다면, RegisterClass()를 통해 등록해줘야 한다.

RegisterClass() 함수의 매개변수는 한 개이고, 이 매개변수에 WNDCLASS 구조체의 주소를 넘겨주는 것이다.

위의 사진을 보면 100228B 주소에서 PUSH EAX 명령을 수행하는데, EAX에는 6FE98 주소가 담겨있고, 이 EAX 레지스터의 값을 스택에 넣는 것으로 보아 WNDCLASS 구조체의 주소는 6FE98이다.

100230F 주소 - CreateWindowExW()


F8 키를 눌러 진행하다보면 100230F 주소에서 CreateWindowExW() 함수를 호출한다.

WNDCLASS 구조체에 값을 넣고, RegisterClass() 함수를 이용해 윈도우 클래스를 등록했다면, CreateWindow() 함수를 호출하여 실제 윈도우 창을 생성한다.

하지만 CreateWindow() 함수를 호출했다고 해서 생성된 윈도우 창을 볼 수 있는 것은 아니다.

아직은 메모리 상에서만 있을 뿐이므로 ShowWindow() 함수를 호출하여 해당 윈도우 창을 화면에 출력해야 한다.

100234D 주소 - 100367A 주소의 함수 호출(지뢰찾기 게임 영역 준비, 지뢰 위치 랜덤하게 선정 및 지정)


F8키를 눌러 진행하면 ShowWindow() 함수를 호출하기 전에 100231C 주소의 JNZ 명령이 수행되어 1002325 주소로 점프하고

이어서 1002332 주소의 JNZ 명령이 수행되어 1002342 주소로 점프하게 된다.

그리고 1002348 주소와 100234D 주소에서 1003CE5 주소의 함수와 100367A 주소의 함수를 호출하는데, 1003CE5 주소의 함수는 딱히 안 봐도 되지만, 100367A 주소의 함수는 내부로 들어가본다.


100367A 주소의 함수 내부는 위와 같다.

총 3번의 CALL 명령이 있는데 한 개는 1002ED5 주소의 함수를 호출하는 것이고, 나머지 두 개는 1003940 주소의 함수를 호출하는 것이다.

그리고 루프가 총 2개가 있는데, 하나는 10036EE 주소에서 10036C7 주소로 분기하는 루프와 다른 하나는 1003703 주소에서 10036C7 주소로 분기하는 루프이다.

처음부터 차근차근 호출되는 두 함수의 내부와 두 루프를 분석해본다.

10036B2 주소 - 1002ED5 주소의 함수 호출(지뢰찾기 게임 영역 초기화 및 세팅)


1005334 주소에 EAX값을 넣고, 1005338 주소에 ECX 값을 넣는데 EAX와 ECX는 모두 16진수 9이다.
(이때 EAX와 ECX는 초급, 중급, 고급 난이도에 따라 값이 달라지는데, 지뢰찾기.exe에서 블럭의 가로 개수 인 것 같다.)

그리고 10036B2 주소에서 1002ED5 주소의 함수를 호출하는데 1002ED5 주소의 함수 내부로 간다.


1002ED5 주소의 함수 내부로 가면 위와 같은데, 이 1002ED5 주소의 함수는 결과적으로 미리 말하자면 사용자가 지뢰찾기 게임을 진행할 영역을 초기화하고 세팅하는 곳이다.

이 1002ED5 주소의 함수 동작을 분석해본다.


1002ED5 주소에서 EAX에 16진수 360을 담고, 1005340 주소부터 10056A0(1005340 + 360) 주소까지 0F로 초기화하는 루프를 돈다.

루프를 돌기 전 EAX 레지스터의


위의 1002F03 주소부터 1002F0F 주소까지 Bh(10진수로 11)번 루프를 돌며 1005340 주소 ~ 100534A 주소까지 그리고 1005480 주소 ~ 100548A 주소까지 10으로 채운다.

루프를 돌기 전 EAX와 EDX 레지스터의 값


위의 1002F2A 주소부터 1002F37 주소의 루프를 돌면서 10054AA 주소와 10054A0 주소에서 각각 16진수 20씩 뺀 주소에 10을 넣는데, Bh(10진수로 11)번 반복하여 10을 넣는다.


그리고 1002F3A 주소의 RETN 명령에 의해 10036B27 주소로 복귀된다.

즉 여기까지 본 내용을 정리하자면, CreateWindow() 호출 후 100234D 주소에서 100367A 주소의 함수를 호출한 뒤
100367A 주소의 함수 내부위 10036B2 주소에서 1002ED5 함수를 호출하게 되는데 이 1002ED5 주소의 함수는 지뢰찾기.exe에서 사용자가 블럭을 클릭하여 게임을 진행할 영역을 초기화하고, 세팅하는 곳이다.

10036CD 주소 - 1003940 주소의 함수 호출(rand() 호출)


이어서 10036CD 주소에서 1003940 주소의 함수를 호출하는데, 인자로 1005334 주소에 있는 값 9h를 넘긴다.

F7 키를 눌러 1003940 주소의 함수 내부로 들어간다.


103940 주소의 내부는 위와 같다.

rand() 함수를 호출하고 있다.

그리고 CDQ 명령어를 수행하는데, 이 CDQ 명령어는 Convert from EAX(4byte) to EDX:EAX(8byte) greatly의 약자로

EAX에 있는 4byte 데이터를 어떠한 연산을 한 뒤 EDX:EAX에 걸쳐 8byte로 저장하는 것이다.
(CDQ 명령이 수행되면 EDX 레지스터의 값이 0으로 설정된다고 한다.)

쉽게 말하자면 CDQ 명령어는 다음 명령으로 오는 연산(나눗셈)을 위해 크기를 확장하는 것이다.

바로 다음 명령어로 IDIV라는 나눗셈 연산을 수행하는 명령이 나오는데, 이 나눗셈 연산을 수행한 결과로 나오는 몫과 나머지를 EDX(나머지)와 EAX(몫)에 저장하는 것이다.
(나눗셈 연산은 EAX 레지스터와 EDX 레지스터에 의해서만 수행할 수 있다고 한다.)


rand() 함수를 호출하면 EAX에는 6D46 값이 담기게 되고


CDQ 명령으로 확장한 뒤 IDIV 명령어를 수행하는데 6D46을 9h으로 나눈다.

나머지


1003947 주소의 IDIV 명령이 수행되면 위와 같이 EAX에는 C24, EDX에는 2이 담기는데, 이는 실제 계산기로 계산해봐도 위와 같다.


정리하자면, rand() 함수를 호출하고 나면 랜덤값(6D46)이 EAX에 들어있고, CDQ 명령어를 수행하여 4byte EAX 레지스터에서 8byte EDX:EAX로 확장한 후 이 상태에서 IDIV 명령이 수행되면 몫(C24)과 나머지(2)가 나오게 되고, 몫은 EAX에 나머지는 EDX에 들어가게 된다.


그리고 위와 같이 100394B 주소에서 나머지 값(2)을 EAX에 옮기고 함수를 종료한다.

10036DB 주소 - 한 번 더 1003940 주소의 함수 호출(rand() 호출)


함수가 종료되고 나면, 10036D2 주소로 복귀되는데 1005338 주소에 있는 데이터 9h를 스택에 넣고, 1003940 주소의 함수의 반환값(2)을 ESI에 담은 뒤 증가시키고 다시 한 번 1003940 주소의 함수를 호출한다.

두 번째 1003940 주소의 함수 호출에서 rand() 함수 호출 후 EAX 값
IDIV 명령어 수행 후 나머지 값을 EAX에 옮긴 후


두 번째 1003940 주소의 함수를 호출하면 위와 같이 EAX에는 3이 담기게 됐다.

10036EE 주소 - 첫 번째 루프(지뢰 위치 선정)


두 번째 1003940 주소의 함수를 호출하는 부분이 지나면 위와 같이 첫 번째 (작은)루프가 나온다.

두 번째 1003940 주소의 함수를 호출한 뒤 EAX에는 반환값이 담겨있게 되는데 이 값을 1 증가시키고 해당 값에 5만큼 Shift Left 연산을 한 후 1005340 주소로부터 (shift Left 연산한 값 + ESI의 값)한 값에 해당하는 주소에 있는 값과 80을 AND연산한다.
(ESI에는 첫 번째 1003940 주소의 함수를 호출한 후 반환된 값을 1 증가시킨 값이다.)

AND 연산한 값이 0이면 ZF 플래그는 1로 설정이 되고, AND 연산한 값이 1이면 ZF 플래그는 0으로 설정이 될 것인데
즉, 80과 값이 같으면 AND 연산 결과는 1이니 ZF 플래그는 0으로 설정, 80과 값이 다르면 AND 연산 결과는 0이니 ZF 플래그는 1로 설정이 된다.

JNZ는 이전 연산 명령의 결과가 0이 아니거나 ZF 플래그가 0이어야 점프하는데, 80과 값이 같으면 AND 연산 결과는 1이므로 연산 결과가 0이 아니니 10036C7 주소로 점프한다.

그렇다면 두 번째 (큰)루프의 명령이 수행되려면 80과 값이 달라야 한다는 것이다.

1003703 주소 - 두 번째 루프(지뢰 위치 지정)


ECX+ESI+1005340 주소에 있는 값이 80이 아니면 이전에 1 증가된 EAX 값을 SHL 연산한 뒤 EAX+ESI+1005340 주소에 있는 값을 80과 OR 연산하여 8F로 만들고, 1005330 주소에 있는 값(Ah == 10진수로 10)을 1 감소시킨다.
(80과 OR연산하여 8F로 만드는 걸 아는 이유는 이전에 1002ED5 주소의 함수에서 지뢰찾기 게임에 사용될 영역을 0F로 초기화했었기 때문에 80이 아니라면 0F 일 것이고, 0F와 80을 OR 연산하면 8F가 되기 때문이다.)

CreateWindow() 호출 후 100234D 주소에서100367A 주소의 함수를 호출하는데 해당 함수 내부


다시 100367A 주소의 함수 내부를 정리하자면, 10036B2 주소에서 1002ED5 주소의 함수를 호출하는데, 이 1002ED5 주소의 함수는 사용자가 지뢰찾기 게임을 진행할 영역을 초기화 하고 세팅하는 부분이다.

그리고 10036C7 ~ 1003703 주소의 루프를 통해 [ECX+ESI+1005340] 주소에 있는 값이 80이 아니면, [EAX+ESI+1005340] 주소에 있는 값을 80과 OR 연산하여 8F로 만드는 작업을 초급, 중급, 고급 난이도에 따라 지정할 지뢰 개수(10, 40, 99)만큼 반복한다.
([ECX+ESI+1005340] 주소와 [EAX+ESI+1005340] 주소는 같은 주소를 가리키게 되는데 미리 80인지 검사하기 위한 알고리즘이다.)

즉, 1002ED5 주소의 함수로 사용자가 게임을 진행할 영역을 초기화 및 세팅하여 준비하고, 10036C7 ~ 1003703 주소의 루프를 통해 지뢰를 랜덤으로 배치하는 것이다.

1003740 주소 - 100346A 주소의 함수 호출(GetDC(), ReleaseDC() 호출)


그리고 1003705 ~ 1003717 주소에서 1005338 주소에 있는 값 9와 1005334 주소에 있는 값 9를 곱한 값 51에서 10056A4 주소에 있는 0A를 뺀 값 47을 ECX에 저장한다.
(1005338 주소와 1005334 주소의 값은 초급, 중급, 고급 난이도에 따라 행과 열의 수이고, 10056A4 주소의 값은 난이도마다의 지뢰 개수이다.)

그 후 1003719 ~ 1003736 주소에서 스택에 EDI 값을 넣고, 각 주소들에 레지스터들의 값들을 넣은 뒤 1003740 주소에서 100346A 주소의 함수를 호출하는데

100346A 주소의 함수 내부의 1003474 주소에서 1002801 주소의 함수를 호출하고, 1002801 주소의 함수 내부에서는 GetDC()와 ReleaseDC()를 호출한다.

F8 키를 눌러 1003746 주소에서 1001950 주소의 함수까지 호출한 뒤 100374E 주소에서 RETN 명령을 수행한다.

1002359 주소 - ShowWindow() 호출, 1002365주소 - UpdateWindow() 호출


RETN 명령이 수행되면 1002352 주소로 복귀되고, 1002359 주소에서 ShowWindow() 함수를 호출하고 이어서 바로 UpdateWindow() 함수를 호출한다.

ShowWindow() 함수의 인자로 화면에 출력하고자 하는 윈도우의 핸들인데, 이 핸들은 위에서 CreateWindowsExW() 함수의 반환값(120190)이다.

즉, CreateWindow() 함수가 리턴한 핸들값(120190)을 그대로 ShowWindow() 함수의 인자로 주면 된다.

그리고 두 번째 인자로는 SW_SHOWNORMAL이 오는 것으로 보아 보통적으로 윈도우를 활성화시켜서 보여준다는 것이다.

UpdateWindow()는 업데이트 할 윈도우의 핸들을 받는 매개변수 하나만 있는데 CreateWindow() 함수의 반환값(120190)을 인자로 주고 있다.

10023AB 주소 - GetMessageW() 호출


UpdateWindow() 함수의 호출까지 끝나고 10023A4 주소로 점프한다.

즉, ShowWindow() 호출 후 UpdateWindow() 호출한 뒤 메시지 루프로 간 것이다.


EDI의 값 0을 두 번 스택에 넣고, 10023A6 주소에서 6FEC0 주소를 EAX에 넣고 EDI의 값을 한 번 더 스택에 넣은 뒤 6FEC0을 스택에 넣고 ESI 레지스터에 있는 함수를 호출하는데, ESI 레지스터에 있는 함수는 GetMessageW()이다.


GetMessageW()의 매개변수는 총 4개이고, 가장 첫 번째 매개변수가 중요한데
즉, Ollydbg의 어셈블리어에서는 GetMessageW()를 호출하기 바로 직전에 스택에 넣는 EAX가 첫 번째 인자이고, 이 인자의 값은 6FEC0이므로 해당 주소의 데이터를 보면 위와 같다.

6FEC0 주소에 있는 데이터가 MSG 구조체의 데이터로 추정할 수 있다.

1002379 주소 - 메시지 루프


https://learn.microsoft.com/ko-kr/windows/win32/api/winuser/nf-winuser-getmessage
GetMessage() 함수의 반환값은 Bool 형으로 True 아니면 False를 반환하며, WM_QUIT 메시지를 받았다면 False를 반환하고, WM_QUIT 이외의 메시지를 받았다면 True를 반환한다.

이제 WM_QUIT 메시지를 받아 GetMessage()가 False를 반환하기 전까지 1002379 ~ 10023AD 주소를 계속 반복하게 된다.


동적 분석

 


1002379 주소에 F2를 눌러 BP를 걸고 F9를 여러 번 누르면 위와 같이 지뢰찾기.exe 윈도우가 띄워지게 된다.

이 상태에서 블럭에 마우스를 위치시키고 연속으로 두 번 클릭해야 하는데

첫 번째 클릭은 지뢰찾기.exe 윈도우를 선택하는 것이고, 두 번째 클릭 때가 지뢰찾기.exe의 블럭에 작업하는 것이다.
(Ollydbg에서 작업하다가 지뢰찾기.exe에 작업하려니 두 번 클릭을 해줘야 하는 것이다.)
(사람마다 한 번만 클릭해도 되는지 두 번 클릭해야 두 번째 발생되는 메시지들에서 동작이 되는지는 다를 수도 있다.)
(아래의 글을 읽다보면 언제부터가 분석 시작 시점인지를 알게 될 텐데, 블럭이 눌려졌다는 게 표시가 되고나서 WM_LBUTTONUP 메시지가 보이면 그 때부터가 분석 시작 시점이다.)

WM_TIMER 메시지 발생


지뢰찾기.exe에서 아무 동작(클릭)을 하지 않으면 WM_TIMER 메시지가 발생된다.

F8 키와 F9 키를 눌러 pMsg 값을 보고 1002379 주소에 멈추게 하는 작업을 반복하면 계속 WM_TIMER 메시지가 발생하는 것을 알 수 있다.

WM_MOUSEMOVE 메시지 발생


위에서 마우스 왼쪽 버튼을 연속으로 총 2번 클릭해야 한다고 했었고, 두 번 클릭한 뒤 Ollydbg에서 F8 키를 누르면

첫 번째 클릭을 위한 WM_MOUSEMOVE 명령어가 발생한다.

가볍게 F9 키로 넘기면 된다.

첫 번째 WM_LBUTTONDOWN 메시지 발생


F8 키를 눌러 pMsg의 값을 보면 WM_LBUTTONDOWN 메시지가 발생됐는데, 이 역시 F9 키를 눌러 넘기면 된다.

첫 번째 WM_LBUTTONUP 메시지 발생


F8 키를 눌러 pMsg의 값을 보면 WM_LBUTTONUP 메시지가 발생됐는데, F9 키를 눌러 넘긴다.

두 번째 WM_LBUTTONDOWN 메시지 발생


그리고 F8 키를 눌러 pMsg의 값을 보면 WM_LBUTTONDOWN 메시지가 발생된 것을 볼 수 있는데 이 때부터가 분석을 시작해야 할 시점이다.
(WM_MOUSEMOVE 메시지가 나올 수도 있는데 이 때는 연속으로 2번이 아니라 한 번 클릭하고 마우스가 살짝 움직인 뒤 다시 한 번 클릭 된거라서 F9 키로 넘기고 F8 키를 누르면 된다.)

왜 그런지는 F9 키를 눌러보면 위와 같이 1,4 좌표에 블럭이 눌렸다는 것이 표시되는 것을 확인함으로써 알 수 있다.

두 번째 WM_LBUTTONUP 메시지 발생


그리고 F8 키를 눌러 pMsg 값을 보면 WM_LBUTTONUP 메시지가 발생됐는데 F9 키를 눌러보면 위와 같이 블럭들에 이미지(?)가 표시되는 것을 확인할 수 있다.


즉 메시지 발생 순서는 아래와 같다.

// 항상 발생
WM_TIMER

WM_MOVEMOUSE

WM_LBUTTONDOWN
	MSG(C03E) wParam = 12, lParam = 0
WM_LBUTTONUP

// (분석 시작 시점)
WM_LBUTTONDOWN
	MSG(C03E) wParam = 12, lParam = 0
WM_LBUTTONUP


그리고 두 번째 WM_LBUTTONDOWN 메세지가 발생했을 때 블럭이 눌려진 것처럼 표시가 되고
두 번째 WM_LBUTTONUP 메시지가 발생했을 때 블럭들에 이미지(?)가 표시되므로
지뢰를 눌렀을 때 다른 지뢰들 위치까지 보이게 하는 부분은 WM_LBUTTONUP 메시지가 발생했을 때 일 것이다.


 

두번째 LBUTTONDOWN 메시지 발생했을 때 분석


먼저 WM_LBUTTONDOWN 메시지가 발생됐을 때를 분석한다.

1002379 주소에 BP가 걸린 상태에서 F9 키를 눌러 지뢰찾기 프로그램을 실행한 후 프로그램이 뜨면 클릭하고자 하는 블럭에 마우스를 위치시키고 두 번 클릭한다.

그러면 첫 번째 발생되는 WM_LBUTTONDOWN과 WM_LBUTTONUP 메시지는 넘기고, 두 번째 발생되는 WM_LBUTTONDOWN부터 분석을 하면 된다.

TranslateMessage() 까지는 그저 메시지를 받고 가공처리하는 것이고, DispatchMessage()에 의해 해당 메시지가 프로그램에게 전달되기 때문에 100239E 주소까지 F8 키로 진행한다.

그리고 100239E 주소에서 DispatchMessageW()를 호출하는데 F7 키를 눌러 내부로 들어간다.
(DispatchMessageW()내에서 운영체제에 의해 WndProc()를 호출하게 된다.)


그러면 위와 같이 나오는데 7500CC3B 주소에서 7500CB03 주소의 함수를 호출한다.
(유저 영역의 주소(1xxxxxxx)가 아닌 주소(7xxxxxxx)는 사용자마다 다를 수 있다.)

7500CC3B 주소에서 7500CB03 주소의 함수를 호출하기 전에 스택에 인자를 두 개 넣는데 하나는 0이고 다른 하나는 [EBP+8]에 있는 값이다.
([EBP+8] = [6FE88] = 6FEC0 = MSG 구조체 데이터)

F7 키를 눌러 7500CB03 주소의 함수 내부로 들어간다.


7500CB03 주소의 함수 내부는 위와 같다.

윈도우 메시지들은 아래의 링크에서 확인할 수 있다.
(https://sean.tistory.com/383)

7500CB44 주소에서 EAX에 있는 값 201(WM_LBUTTONDOWN)과 400(WM_USER)을 비교한다.

400보다 작으면 7500CC86 주소로 점프하는데, 201은 400보다 작으니 점프한다.


7500CC86 주소로 가면 위와 같다.

[EAX+7500C650] 주소에 있는 값과 80이 AND 연산 후 1이면, 7502B3AA 주소로 점프인데, 00과 80은 같지 않으므로 AND 연산 결과 0이고, ZF 플래그가 1로 세팅되므로 JNZ 명령이 수행되지 않아 219와 비교한다.

EAX(201)와 219는 같지 않으므로 218과 비교하고, 218도 아니므로 cmp 결과 1이고, ZF 플래그가 0으로 세팅되므로 JNZ 명령이 수행되어 7500CB4F 주소로 점프한다.


그리고 201과 113, 118, 0F, 119 등 비교하는데 모두 맞지 않아 패스되고

7500CBE4 주소에서 7500C504 주소의 함수를 호출하는데 인자로 1001BC9 주소가 넘어가는 걸 볼 수 있고
이 1001BC9 주소는 유저 영역의 주소이기 때문에 7500CBE4 주소에서 F7 키를 눌러 7500C504 주소의 함수 내부로 들어간다.

7500C504 주소의 함수 내부 1
7500C504 주소의 함수 내부 2

 

7500C586 주소에서 호출하는 7500C617 주소의 함수 내부로 들어가기 직전
두 번째 인자


7500C504 주소의 함수 내부는 위와 같은데 위의 첫 번째 사진 속 맨 마지막 쯤에 7500C586 주소에서 호출하는 7500C617 주소의 함수 내부로 들어간다.
(7500C617 주소의 함수를 호출하기 전 두 개의 인자를 넘기는데 그 중 하나는 WM_LBUTTONDOWN 메시지의 정수값(201)이다.)


7500C617 주소의 함수 내부에서 F8 키를 눌러 진행하면 그냥 7500C64B 주소에서 RETN 8 명령에 의해 복귀 주소로 간다.


복귀 주소로 가면 위와 같이 7500C58B 주소로 가게 된다.

그렇다면 7500C586 주소에서 호출하는 7500C617 주소의 함수는 원하는 부분이 아니라는 것이다.

위의 사진 속 1001BC9 주소를 인자로 넘기고 함수를 호출하는 부분이 한 군데 더 있다.

7500C5B2 주소에서 호출하는 7500C494 주소의 함수이다.

F7 키를 이용해 7500C494 주소의 함수 내부로 간다.


그러면 위와 같다.

9개의 값을 스택에 넣고 7500C4B4 주소에서 [EBP+8] 주소에 있는 함수를 호출한다.

7500C4B4 주소까지 F8 키로 진행 후 [EBP+8] 주소에 있는 값은 1001BC9 주소이다.

그렇다면 F7을 누르면 1001BC9 주소로 갈 수 있다.


그러면 위와 같이 나온다.

7500C5B2 주소에서 호출하는 7500C494 주소의 함수 내부에서 1001BC9 주소를 호출하는 부분이 있었던 것이다.

1001BC9 주소로 오게 된 루트를 정리하면 아래와 같다.

100239E 주소에서 DispatchMessageW() 호출
	->7500CC3B 주소에서 7500CB03 주소의 함수 호출
		-> WM_USER, WM_TIMER, WM_SYSTIMER 등의 메시지들과 비교 후 조건 분기들을 넘긴 뒤
			-> 7500CBE4 주소에서 7500C504 주소의 함수 호출
				-> 7500C5B2 주소에서 7500C494 주소의 함수 호출
					-> 7500C4B4 주소에서 1001BC9 주소의 함수 호출

 


1001BC9 주소의 끝 부분을 보면 위와 같은데 DefWindowProc()가 있는 것으로 보아 1001BC9 주소가 WndProc() 함수이다.


첫 번째 조건 분기는 1001BE4 주소에서 일어나는데, 201과 200을 비교하는것이다.

비교 후 앞에 있는 레지스터의 값이 뒤에 있는 레지스터의 값보다 크면 1001F5F 주소로 점프하는데, 201이 200보다 크므로 점프한다.


1001F5F 주소는 위와 같다.

EDX(201) - 201 한 값을 EAX에 넣고, 11과 비교 후 11보다 크면 10021A9 주소로 점프하는데


10021A9 주소는 위와 같이 DefWindowProcW()를 호출하는 곳으로, EDX - 201 한 값이 11보다 크면 case 문에 지정되지 않은 값이므로 DefWindowProc()이 처리하도록 한다는 것이다.


WM_LBUTTONDOWN 메시지가 발생했을 때는 11보다 작으므로 따로 처리하도록 case 문이 작성되어 있는 것 같다.

1001FA6 주소로 점프하는데 F8 키를 눌러 쭉 진행하다보면 1001FDA 주소에서 100205F 주소로 점프한다.


100205F 주소로 점프 후 100207D 주소에서 1002913 주소의 함수를 호출하고 나면 위와 같이 지뢰찾기 프로그램의 스마일 얼굴이 놀란 표정으로 바뀌게 된다.

그리고 F8 키를 눌러 쭉 진행하다가 10020B1 주소에서 10031D4 주소의 함수를 호출하는데 F7 키를 눌러 이 함수 내부로 들어간다.


10031D4 주소의 함수의 내부는 위와 같다.

F8 키를 눌러 쭉 트레이싱 하다보면


위와 같이 1003410 주소에서 1002646 주소의 함수를 호출하고, 이 함수가 호출되고 나면 위와 같이 지뢰찾기 화면에 출력이 된다.


그렇다면 위와 같이 Enter 키를 이용해 1002646 주소의 함수 내부를 봤을 때 GetDC(), BitBlt(), ReleaseDC() 함수들이 호출된다.


이후 F8키를 이용해 진행하다보면 10023A4 주소로 오게 되고, 이 부분은 메시지 루프 부분으로 GetMessageW()를 호출하는 곳으로 가게 된다.


두 번째 WM_LBUTTONUP 메시지 발생했을 때 분석


F8 키를 이용해 진행하면 위와 같이 WM_LBUTTONDOWN 메시지 다음 WM_LBUTTONUP 메시지가 발생된다.

이제 WM_LBUTTONUP 메시지가 발생됐을 때를 분석한다.


F8 키를 이용해 쭉 트레이싱 하다보면 WM_LBUTTONDOWN 메시지가 발생했을 때와 같에 1001BC9 주소로 오게 된다

하지만 1001F75 주소에서 1001FDF 주소로 점프하게 된다는 점이 다르다.


1001FDF 주소에서 이어서 F8 키로 따라가다 보면 1002005 주소에서 10037E1 주소의 함수를 호출하는데 해당 함수 내부로 들어간다.


10037E1 주소의 함수 내부는 위와 같다.

100392B 주소에서 10038ED 주소의 함수를 호출하는데, 해당 함수 내부로 간다.


3이 아니면 100393D 주소로 점프하여 함수를 종료하는데, 10056B8 주소의 값이 0이므로 3과 같지 않아 100393D 주소로 점프하고 종료한다.

이외에는 PlaySound()를 호출하여 소리를 송출하는 것 같다.


그러면 위와 같이 1003830 주소로 복귀되는데, 100579C 주소의 값을 증가 시키고 10028B5 주소의 함수를 호출하므로 해당 함수 내부로 들어간다.


그러면 위와 같이 나오는데 GetDC()와 ReleaseDC()를 호출하고 있다.

ReleaseDC()를 호출하면 위와 같이 타이머 시간이 1 올라간다.

즉, WM_LBUTTONUP 메시지가 발생했을 때 1003836 주소에서 1028B5 주소의 함수를 호출하면 타이머가 1초 증가되어 표시된다는 것이다.


F8 키를 눌러 복귀 주소로 복귀하면 100383B 주소로 오게 되는데 스택에 인자를 넣고 100384F 주소에서 SetTimer()를 호출한다.



F8 키로 진행하다 보면 10038B1 주소에서 1003512 주소의 함수를 호출하는데 이 주소의 함수가 호출되고 나면 위와 같이 숫자들이 표시된다.

그리고 10038BC 주소에서 1002913 주소의 함수를 호출하는데 이 주소의 함수를 호출하고 나면 스마일 얼굴의 표정이 놀람에서 다시 스마일로 바뀌게 된다.
(위의 사진 속 검은색으로 칠한 부분은 지뢰찾기 윈도우 위에 다른 프로그램의 윈도우를 올리는 바람에 깨져서 출력이 안되어 나중에 찍은거라 타이머만 가렸다.)


그리고 F8 키를 눌러 트레이싱하면 위와 같이 다시 메시지 루프로 오게 된다.


지뢰를 누르고 WM_LBUTTONUP 메시지 발생했을 때 분석

 

10 10 10 10 10 10 10 10 10 10 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 41 40 40 40 40 40 40 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 8F 42 41 41 40 40 40 40 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 0F 8F 41 40 40 40 40 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 41 41 41 40 40 40 40 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 41 40 40 41 41 41 40 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 8F 41 40 41 43 8F 43 41 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 41 40 41 8F 8F 0F 8F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 41 40 41 43 8F 0F 8F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 8F 41 40 40 41 0F 0F 0F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 10 10 10 10 10 10 10 10 10 10 0F 0F 0F 0F 0F


위에서 랜덤으로 지뢰를 배치하는 부분을 분석할 때 1005340 주소부터 랜덤한 곳에 지뢰를 배치했다.

1005340 주소를 보면 위와 같이 되어 있는데 8F가 지뢰의 위치이다.

현재 지뢰찾기에 표시된 숫자들과 위의 메모리 상태를 보면 41은 1을 의미하고, 40은 공백을 의미하고, 42는 2를 의미하고, 43은 3을 의미하는 것 같다.

그리고 메모리에서의 값과 지뢰찾기 프로그램에 보이는 것을 비교해보면 지뢰찾기 프로그램에서의 한 줄은 메모리에서의 두 줄이다.

즉, 지뢰찾기 프로그램에서 보이는 맨 첫 번째 줄은 메모리 맵에서는 아래의 두 줄 이라는 것이다.

10 0F 0F 41 40 40 40 40 40 40 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F

 


Ctrl + F2 키를 눌러 재실행 후 1002379 주소에 BP가 걸린 상태에서 F9 키를 여러 번 눌러 1002379 주소까지 실행하면 위와 같이 지뢰찾기 프로그램이 재실행 될 것이다.

10 10 10 10 10 10 10 10 10 10 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 8F 0F 0F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 0F 0F 8F 0F 0F 0F 8F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 8F 0F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 0F 0F 0F 0F 8F 0F 8F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 0F 0F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 0F 0F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 0F 8F 0F 0F 0F 8F 0F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 0F 0F 0F 0F 0F 0F 8F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 0F 0F 0F 0F 8F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F
0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F
10 10 10 10 10 10 10 10 10 10 10 0F 0F 0F 0F 0F


덤프 창에서 1005340 주소로 가면 위와 같다.

메모리에서는 3번째 줄의 3번째 자리에 8F가 있으니, 프로그램에서는 1,2 좌표가 지뢰일 것이다.


위와 같이 1,2 좌표의 블럭을 누르고, F8 키를 이용해 pMsg 값이 어떤 메시지인지 확인 후 WM_LBUTTONDOWN인지를 확인한다.

pMsg 값이 WM_LBUTTONDOWN이면 F9 키를 한 번 눌러 실행하고
지뢰찾기 프로그램이 위와 같이 1,2 좌표에 블럭이 눌러짐이 보이는지 확인 후 위와 같이 보여지지 않는다면
눌려짐이 보여질 때까지 F8 키와 F9 키를 이용해 반복하고, 위와 같이 눌려짐이 보인다면
F8 키를 눌러 위의 사진처럼 WM_LBUTTONUP 메시지가 발생했는지 확인한다.

그리고 이 상태에서 F8 키로 100239E 주소까지 실행 후 F7 키로 DispatchMessageW 함수 내부로 들어간다.


가장 빠른 방법은 WndProc() 부분인 1001BC9 주소에 BP를 걸고 F9 키를 눌러 한번에 가는 것이지만, F8 키로 진행하며 따라간다.

DispatchMessageW 함수 내부에서 7500CB03 주소의 함수를 호출하는데 해당 함수 내부로 들어간다.


그럼 위와 같이 나오고 F8 키를 눌러 트레이싱 한다.


그러면 위와 같이 7500CBE4 주소에서 7500C504 주소의 함수를 호출하는데 F7 키를 눌러 내부로 들어간다.


그러면 위와 같다.

F8 키를 이용해 진행하다가 7500C5B2 주소에서 7500C494 주소의 함수를 호출하는데 F7 키를 이용해 해당 함수 내부로 들어간다.


그럼 위와 같고, 7500C4B4 주소에서 [EBP+8] 주소의 함수를 호출하는데 F7 키를 이용해 함수 내부로 들어간다.


그러면 드디어 WndProc()인 1001BC9 주소에 도착했다.


1001BE4 주소에서 1001F5F 주소로 점프한다.


202 - 201한 값 1이 11보다 큰지 검사하고, 작으면 1001F75 주소에서 [EAX * 4 + 10021C2] 주소인 1001FDF 주소로 점프한다.


F8 키로 진행하다 보면 ReleaseCapture()를 호출 후 1002005 주소에서 10037E1 주소의 함수를 호출하는데, 해당 함수 내부로 들어간다.


10037E1 주소의 함수 내부에서 F8 키를 눌러 쭉 진행하다보면 10038B1 주소에서 1003512 주소의 함수를 호출하는데 F7 키를 눌러 내부로 들어간다.

그리고 쭉 F8 키를 눌러 트레이싱을 하는데, 뭔가 이상하여 계속 쭉 진행했다.


쭉 진행하여 위와 같아 메시지 루프 1002379 주소로 오니 1,2 좌표에는 1이 표시가 된다?


1005340 주소를 보니 이전에 위에서 3번째 줄 3번째 자리에 8F가 있었는데 지금 보니 3번째 줄 2번째 자리에 있다?


그렇다면 지뢰찾기 프로그램에서 1로 표시된 자리의 왼쪽 자리인 1,1 좌표의 블럭을 두 번 연속으로 누르고 두 번째 WM_LBUTTONUP 메시지가 발생할 때 까지 F8 키와 F9를 누른 뒤 두 번째 WM_LBUTTONUP 메시지가 발생했을 때 다시 분석을 한다.


Ctrl + G 키를 이용해 1001BC9 주소로 이동한 뒤 F2 키를 이용해 BP를 걸고 F9 키를 눌러 실행해 1001BC9 주소까지 온다.


1001BC9 주소부터 F8 키를 눌러 진행하다 보면 10038B1 주소에서 1003512 주소의 함수를 호출한다.

해당 함수 내부로 간다.


여기 부분이 지뢰인지 판단하는 부분이다.

1003512 주소의 함수 내 1003529 주소에서 EDX의 값 즉, 클릭한 블럭에 있는 값이 80이면, 10057A4 주소에 있는 값이 0인지 비교 후 0이 아니면 1003588 주소로 점프인데, 10057A4 주소의 값은 1이므로 1003588 주소로 점프한다.

그리고 100358C 주소에서 1002EAB 주소의 함수를 호출하는데 해당 함수 내부로 들어간다.


그러면 위와 같은데

클릭한 블럭의 주소에 있는 값 80을 가져와 0E0과 AND 연산 후 해당 값에 4C와 OR 연산하여 CC로 만든다.

CC는 폭탄 그림을 의미하는 것 같고, CC를 다시 클릭한 블럭의 주소에 넣은 뒤 1002ECD 주소에서 1002646 주소의 함수를 호출한다.

(CC가 폭탄 그림을 의미하는 지는 명확한 확인 작업이 필요하나, 폭탄 그림의 ID가 아닐까 하는 의심이 든다.)

 


102646 주소 내부에서는 GetDC(), BitBlt() 그리고 ReleaseDC()를 호출하여 숫자 또는 지뢰를 화면에 출력하는데, 여기서는 지뢰를 출력한다.

ReleaseDC() 호출까지 완료되면 복귀 주소로 복귀하는데


1002EAB 주소의 함수 내 1002ED2 주소로 복귀되고 이 주소의 RETN 0C 명령이 수행되면


1003512 주소의 함수 내 1003591 주소로 복귀 후 1003593 주소에서 10035AB 주소로 점프한다.


10035AB 주소에서는 100347C 주소의 함수를 호출하는데 해당 함수 내부로 간다.


그러면 위와 같이 나온다.


F8 키를 눌러 진행하다가 1003497 주소에서 1002913 주소의 함수를 호출하는데


Enter 키를 이용해 1002913 주소의 함수를 보면 위와 같이 GetDC()와 ReleaseDC()를 호출한다.

딱히 중요하지 않으므로 F8 키를 눌러 내부로 들어가지 않고 진행한다.


이어서 10034AB 주소에서 1002F80 주소의 함수를 호출하는데 해당 함수 내부로 들어간다.


1002F80 주소의 함수 내부에서는 이중 루프를 도는데, 이 이중 루프 중 안 쪽 루프는 지뢰를 의미하는 8F 값을 8A로 바꾸는 것이고, 바깥 쪽 루프는 1005360 주소에 20h을 증가시킨다.

이전에 위에서 프로그램에서의 한 줄은 메모리 상에서 두 줄이라고 했다.

즉, 프로그램에서 첫 번째 줄은 메모리 상에서 3번째 줄이고, 프로그램에서 두 번째 줄은 메모리 상에서 5번째 줄이다.

그러므로 바깥 쪽 루프에서는 20h씩 증가시켜 메모리 상에서 다다음줄로 가게 하는 것이고, 안쪽 루프에서는 해당 줄에서 8F 값이 있으면 그 값을 8A로 바꾸는 것이다.


최종적으로 위의 이중 루프를 다 돌고 나면 위와 같다.

그리고 1002FD8 주소에서 100272E 주소의 함수를 호출하는데 해당 함수 내부로 간다.


그러면 위와 같이 GetDC()와 RelaseDC()를 호출하는데 그 사이에 10026A7 주소의 함수를 호출한다.


Enter 키를 이용해 10026A7 주소의 함수 내부로 가면 BitBlt()를 반복적으로 호출하는데


F8 키를 이용해 10026A7 주소의 함수 내부로 들어가지 않고 실행하면 위와 같이 지뢰들이 표시된다.

이어서 ReleaseDC()를 호출하고 F8 키를 계속 누르다 보면


위와 같이 다시 메시지 루프로 오게 된다.

전체 지뢰를 출력하는 부분까지의 경로를 정리하면 아래와 같다.

1001BC9 주소의 함수 내 10038B1 주소에서 1003512 주소의 함수를 호출
    -> 1003512 주소의 함수 내 1003529 주소에서 클릭한 블럭의 값이 지뢰 값인지 확인
    ->  1003512 주소의 함수 내 100358C 주소에서 1002EAB 주소의 함수를 호출
        -> 1002EAB 주소의 함수 내에서 지뢰가 위치한 곳에 지뢰를 의미하는 값 CC를 넣는다.
        -> 1002EAB 주소의 함수 내 1002ECD 주소에서 1002646 주소의 함수를 호출(지뢰 출력)
    -> 1003512 주소의 함수 내 10035AB 주소에서 100347C 주소의 함수를 호출
        -> 100347C 주소의 함수 내 10034AB 주소에서 1002F80 주소의 함수를 호출
            -> 1002F80 주소의 함수 내에서 전체 영역에서 지뢰가 위치한 곳의 값들을 8F에서 8A로 변환(전체 지뢰 탐색)
            -> 1002F80 주소의 함수 내 1002FD8 주소에서 100272E 주소의 함수를 호출
                -> 100272E 주소의 함수 내 100273E 주소에서 10026A7 주소의 함수 호출(BitBlt()를 이용해 전체 지뢰 출력)

 


프로그램 시작과 동시에 전체 지뢰 위치가 보이도록 패치

 


1002379 주소의 메시지 루프에서 1002386 주소에 JMP 문으로 수정해 인라인 패치를 할 것이다.


먼저 Code Cave를 설치할 곳을 찾기 위해 .text 영역인 1004A60 주소를 보면 NULL 영역인 것을 볼 수 있다.

1004A60 주소에 Code Cave를 설치하도록 한다.


1002386 주소에 있던 CALL DWORD PTR DS:[1001150] 명령어는 복사하여 백업해둔 뒤 위와 같이 jmp 1004A60 문으로 수정한다.

push 0a
call 1002f80
CALL DWORD PTR DS:[1001150]
jmp 100238b


1004A60 주소에는 위와 같이 위의 명령을 기입하고

(push 0a를 하는 이유는 위에 1002f80 주소의 함수를 호출하는 부분을 분석한 것을 보면 push eax를 하는데, 이때 eax에는 0a가 담겨있기 때문이다.)

마우스 우클릭 -> Copy to executable -> All modifications -> copy all -> 마우스 우클릭 -> save file -> 이름 지정 -> 저장


그리고 패치된 파일을 실행하면 위와 같이 잘 실행된다.




반응형

+ Recent posts