티스토리 뷰
Timer 조작
실습환경 : VM - Window7 32bit
초급으로 실행시킨뒤 Ollydbg에 attach시켜보자.
C:\Program Files\Microsoft Games\Minesweeper 폴더안의 파일을 직접 찾아도 됨.
디버거 타이틀을 살펴보면 보통 다른 thread와 module을 가리키고 있다.
우클릭 - select module - minesweeper을 선택해 바꾼다.
SetTimer()함수를 찾아야한다.
우클릭 - search for - all intermodular calls 에서 dest name으로 정렬시켜 USER32.SetTimer()을 찾아낸다.
BP를 걸고 F9로 실행시킨다. 게임이 동작하고 타일을 하나 클릭하면 다시 해당 BP에서 멈춘다.
MSDN
3번째 인자인 uElapse에 주목하자. 타임아웃 시간간격을 결정하며 milliseconds 단위이다.
즉 1000이 들어가면 1초를 의미한다.
다시 디버거에서 BP가 걸려 해당 SetTimer()가 호출된 부분을 살펴보면, 4번의 push로 인자를 넘겨주고 그 중에 'push 3e8' 명령어를 발견할수있다.
헥사코드 3e8은 정수로 1000을 의미한다. 이 값을 5000을 의미하는 1388로 바꿔보자.
수정후 edit - copy all modifications to executable로 별도 저장한다. 이때 파일형식을 Executable file or DLL로 저장한다.
C:\Program Files\Microsoft Games\Minesweeper 폴더안에서 새로 저장한 게임파일을 실행시켜본다.
1초가 아닌 5초마다 타이머가 1씩 증가하는것을 확인할수있다.
맵핵
가정1. 타일을 클릭시 SetTimer() 함수가 호출된다. 이 함수로부터 지뢰설치 로직 추적
가정2. 지뢰 타일을 클릭시 게임을 다시 묻는 창을 찾는다. GetDlgItem() 함수가 이 창을 열고, 이 함수에 BP를 걸어 추적한다.
가정3. 타일을 클릭시 호출되는 GetKeyState() 함수에 BP를 걸어 추적한다.
GetKeyState() 함수는 가상키(Virtual Key) 값을 인자로 받아 가상키의 상태를 숫자로 반환한다.
즉, 지뢰찾기 프로그램에서는 타일을 가상키로 간주하고 상태를 해당함수를 통해 체크한다.
우클릭 - search for - names - GetKeyState를 찾는다 - 우클릭 - find reference
=> GetKeyState()라는 이름을 사용하는 모든 코드를 보여준다.
CALL로 시작하는 직접 USER32.GetKeyState()를 호출하는 방식의 명령어와
MOV EDI, DWORD PTR로 시작하는 간접적으로 호출하는 방식의 명령어를 발견할수있다. 이 경우, 주소를 EDI 레지스터에 저장했다가 나중에 CALL EDI와 같은 방식으로 함수를 호출하는 방식이다.
디버깅관점에서 Search for - All intermodular calls / Names의 두 방식의 차이점은
All intermodular calls는 CALL로 시작되는 직접호출방식만 검색할수있다는 것이다.
따라서 함수가 사용되는 모든 영역을 찾으려면 Names방식이 더 적합하다.
아직 타일클릭시 호출되는 부분을 명확히 알지못하므로 모든 부분에 BP를 건다.
맨처음 BP가 걸리는 00DDB02A는 게임실행전의 시점이다. 해당 함수는 가상키가 눌리는 이벤트를 지속적으로 모니터링 하고있기 때문에, 프로그램이 로딩되면서 함수가 호출될수있다. 따라서 무시하고 BP를 해제한뒤 F9로 넘어간다.
이후 게임이 실행되고, 중간의 한 타일을 선택하면 00DCC7FA에서 BP가 걸린다.
F9로 넘어가면 근처의 00DCC826에서 다시 BP가 걸린다. 이를 다시 F9로 넘어가면 타일은 눌리지 않은 상태다.
다시 한 타일을 선택하면 BP가 다시 걸리며, 이 과정이 반복된다.
이는 해당함수가 가상키가 눌리는 이벤트를 지속적으로 모니터링하기때문에 다음상태로 넘어가지 못하고 있는것으로, 두 부분의 BP를 제거하고 넘어간다.
다시 타일을 선택하면 이번엔 00DCC950에서 BP가 걸리며, 선택한 타일은 뒤집힌 상태이다. 이부분이 의심되어 나머지 BP들을 삭제하고, 이부분을 따라간다.
해당부분을 CMP, TEST같은 비교함수와 JNE, JZ같은 점프함수를 주의하며 따라간다.
00DCC95D, 00DCC976, 00DCC981 세번의 비교함수를 만나, ZF를 바꿔보며 디버깅해서 역할을 확인할수있다. 각각 '타일이 클릭되었는지 확인하는 구문'과 '타일을 우클릭으로 지뢰를 표시했는지 확인하는 구문', '주변의 지뢰가 없는 타일을 뒤집는 구문'의 역할임을 확인하고, 목표와 상관없으므로 모두 F9로 넘어간다.
이후 좀더따라가면 00DCC9C4 : CALL 00DC6FB7 구문을 만난다. Step into로 들어간다.
당연히 스택 프레임을 설정하는 push ebp, mov ebp esp와 같은 명령어들이 나온다.
따라가다보면 00DC6FDE에서 2번째 서브루틴을 만난다. 여기서 들어가며 인자 4와3이 전달되고 복귀주소가 저장된다. 이때, 2번째 서브루틴내에서 retn이 아닌 jmp문으로 종료한다. 따라서 저장한 복귀주소는 나중에 3번째 서브루틴에서 사용되어 3번째 서브루틴이 종료되면 이곳으로 돌아간다. 2번째에서 jmp로 이동하면 3번째 서브루틴으로 이동한다. 편하게 시작부인 00040C50(재시작으로 앞부분주소변경)에 BP를 설정한다.
주석으로 표시한지점들에서 ebx에 3이, edi에 4가 들어가는것을 확인할수있다.
이는 좌상단을 (0,0)기준으로, 아까 내가 선택한 타일이 (3,4)에 해당함을 의미한다.
이어서 따라가다보면, 00040C75 : CMP EAX, 9 문장을 만난다.
이 문장을 통해 ZF가 1로 설정되는데, 임의로 0으로 바꾼뒤 실행해 역할을 알아보면, 다음과 같이 패배창이 뜨는것을 확인할 수 있다.
EAX에 9가 아닌 다른값이 저장되있다면 지뢰를 클릭한것으로 판단한 것이다. 역시 무관하므로 넘어간다.
0C95의 명령어를 코드케이브를 위해 점프시키는것으로 수정한다.
이후 해당 빈 공간에서 동작수행후 다시 0CBA위치로 돌아온다.
* 0CC6 명렁 수행후, 클릭한 타일이 비었으면 ZF=1, 지뢰였으면 ZF=0 이다.
'Security&Hacking > Reversing' 카테고리의 다른 글
Reversing) Lena17. KeygenMe (0) | 2019.09.11 |
---|---|
Reversing) abex crackme #5 (0) | 2019.09.11 |
Reversing) MUP(Manual Unpacking) (0) | 2019.09.11 |
Reversing) abex crackme #4 (0) | 2019.09.11 |
Reversing) DLL Injection with CreateRemoteThread() (0) | 2019.09.11 |
- Total
- Today
- Yesterday
- graph
- Data Structure
- Algorithm
- 리버싱
- 개발자
- sort
- dfs
- queue
- Android Studio
- javascript
- Android
- mysql
- 웹해킹
- 우아한 테크코스
- FRAGMENT
- 프로그래머스
- 해외여행
- reversing
- OneToMany
- Vo
- brute-force
- git
- socket
- webhacking.kr
- bfs
- JPA
- C
- Java
- 회고
- Stack
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |