추억의 게임인 지뢰 찾기 게임을 분석해봤다.
분석 도구는 IDA를 사용했으며, 분석 내용을 토대로 코드 패치를 하여 게임 흐름까지 바꿔보았다.
이 주제의 글은 총 2개로 나누어서 작성하고자 한다. 각 글의 대한 간략한 내용은 다음과 같다.
#1. 지뢰 찾기 분석 + 도움말 함수 코드 패치로 지뢰를 나타내주는 함수 호출
#2. 게임 실행 함수 시, 이미지를 나타내주는 연산 값을 패치하여 지뢰 표시
먼저, 해당 게임은 메모리가 고정적이여서 분석과 구현이 어렵지 않았다.
게임 테이블(지뢰) 세팅
지뢰 세팅 함수(sub_01002F11)
게임이 시작되고, 지뢰를 설정해주는 함수이다. 반복문을 돌면서, 난이도에 맞는 지뢰의 개수만큼 지뢰를 생성한다. 이때, v1과 v2가 sub_1003940 함수의 리턴 값으로 설정된다.
해당 함수는 지뢰가 생성될 좌표 값이다. 난이도에 따라 가로와 세로 길이가 다르기 때문에, 해당 길이를 인자로 받아 랜덤한 값을 리턴한다.
따라서, 가로와 세로의 인덱스를 랜덤으로 생성하게 되고, do~while 문을 통해 중복없이 지뢰를 세팅한다.
게임 테이블(0x1005360)
먼저, 게임 테이블은 적절한 곳에 브레이크포인터(bp)를 걸고 게임을 플레이 하면서 찾을 수 있었다.
지뢰를 찾았을 때, 게임 종료 전에 BitBlt 함수로 각 테이블 값에 맞는 이미지를 화면에 띄워주게 된다.
이때 참조하는 테이블이 0x1005360이다. 해당 주소를 찾아가서 게임창과 비교하면서 확인해보면 각 상태에 대한 값을 확인할 수 있다.
지뢰는 0x8A이고, 숫자는 0x4? 값으로 메모리에 저장되는 것을 확인할 수 있다.
사용자 마우스 클릭 이벤트
WindowProc(sub_1001BC9)
switch~case 문으로 Msg의 값에 따라 다른 행위를 하고 있다.
해당하는 case들은 윈도우에서 정의된 이벤트를 나열하는데, 각 값에 따른 행위는 아래 사진과 같다.
이 외에도 정의된 윈도우 이벤트는 다음 링크에서 확인할 수 있다.
지뢰 클릭(sub_1002646)
지뢰를 클릭하면, 해당 프로그램 어디선가 BitBlt 함수를 통해 지뢰 그림을 표시해줄 것이다. Xref를 통해 BitBlt 함수를 사용하는 곳을 중점으로 브레이크 포인터(bp)를 걸어두고 분석을 했다.
sub_1002646 함수를 호출하는 부분이다. 지뢰를 클릭하면 테이블에서 값이 0x80으로 바뀌고, 이 부분을 체크하면서 폭탄이면 CC로 바뀐다.
bp가 걸린 모습이다. 클릭된 좌표의 지뢰는 표시했지만 테이블 전체의 지뢰가 표시되지 않은 상태이며, 게임 종료 창이 뜨지 않았다.
모든 지뢰 찾기(sub_10026A7)
해당 부분에서 테이블을 참조해서 지뢰 그림을 띄워주는 부분이다.
Xref로 상위 함수를 분석한다.
클릭한 지뢰 부분은 0xCC 이고, 나머지 지뢰들은 0x8A로 알 수 있다.
아! 그리고 그림은 추가하지 못했는데, 나타난 지뢰의 값은 0x8A지만, 숨겨져있는 지뢰의 값은 0x8F로 세팅된다. 위 그림에서는 다 나타난 상태이기 때문에 0x8A로 들어가 있는 것이다.
지뢰 찾기 핵 만들기
앞서 분석에서 sub_10026A7 함수가 모든 지뢰를 나타내주는 함수임을 확인했다. 이 분석 내용을 토대로 트리거 지점을 설정하여 모든 지뢰를 나타낼 수 있도록 메모리를 변조하는 핵 프로그램을 만들어볼 것이다.
sub_10026A7 함수를 호출하는 부분 2곳 있다.
두 함수는 sub_100272e 함수와 sub_1002ac3 함수이다.
이 두 부분에서 sub_10026A7 함수를 호출할 때, 기계어로 어떻게 구성되어 있는지 분석해봤다.
sub_100272e 함수 분석
해당 호출 부분의 E8 64 FF FF FF 이다. E8은 call 명령어임을 나타내고, 64 FF FF FF는 호출되는 함수와 호출하는 함수의 상대적 거리로 구성된다.
10026a7 - 100273e = ff~ff69
ff~ff69 - 5(call instruction length) = ff~ff64
연산된 값에서 호출하는 함수의 주소가 더 크기 때문에 ff~??으로 값이 넘어가게 된다.
call 인스트럭션 길이인 5를 빼주면 0xffffff64 값이 나오게 되고, 이 값으로 sub_10026A7 함수를 호출한다.
sub_1002ac3 함수 분석
또 sub_10026A7 함수를 호출하는 다른 부분이다. 이 역시 앞 식과 동일하게 계산해보면, 호출 부분의 헥사값과 동일한 것을 확인할 수 있다.
10026a7 - 1002ae7 - 5 = 0xfffffbbb
테이블의 모든 지뢰를 표시할 때 호출하는 함수는, sub_10026A7 함수의 상위 콜인 sub_100272E로 호출하면 좋을 듯 하다.
sub_100272E 를 호출하는 부분은 sub_1002F80 함수에서만 호출하므로, 이 함수를 호출하도록 코드를 패치할 것이다.
트리거 : 도움말 표시(sub_1003d76)
지뢰 찾기 게임에서는 F1 키를 누르면 도움말이 표시된다. 해당 함수는 sub_1003d76 함수이다.
도움말은 게임 수행 시 지장이 없기 때문에, 이 부분을 트리거 지점으로 사용했다.
코드 패치(sub_1003d76)
1003d76()
코드 패치 : 1003d7f
eax 0a
b8 0a 00 00 00
call 1002f80
e8 f7 f1 ff ff
1002f80 - 1003D84 -5 = ff ff f1 f7
jmp 1001F14
e9 86 e1 ff ff
1001F14 - 1003d89 - 5 = ff ff e1 86
00 00 00 00 00
끝나면 여기로 점프를 한다.
1004062 - 1003deb - 5 = 00 00 02 72
위 흐름대로 적절하게 패치를 해준다. 패치는 HxD로 헥스 값으로 지점을 찾아서 값을 수정해주었다.
패치된 프로그램
F1 키를 누르면 지뢰가 있는 부분을 그림을 띄워준다. 하지만 지뢰 이미지로는 불러와 지지 않는다..
깃발을 먼저 세우고, 나머지 부분을 눌러서 후딱 풀 수 있었다.