연습은 꾸준히
여태 올린 글들을 정독하면서 꾸준히 해오셨으면 이번 문제도 크게 어려울 건 없어 보입니다.
이번엔 2010년 때 파도콘의 예선 문제였던 걸 한 번 풀어보도록 하죠.
* 파도콘이란?
한국의 대학교 정보보호 및 컴퓨터 보안 동아리의 연합회라고 합니다.
저도 처음 들어봤어요.
아무튼 나름 공신력 있는 곳에서 냈던 문제 같으니까 도움이 될 거 같습니다.
프로그램을 켜보니까 뭐 이렇게 생겼습니다.
3개의 숫자를 다 맞추면 통과인 거 같습니다.
여태 프로그램과는 달리 방어 로직이 있다
근데 이번 프로그램에서의 재밌는 점은 이 프로그램은 ollydbg.exe의 실행여부를 검사해서
실행이 되어 있다면 프로그램을 종료한다는 점입니다.
ollydbg.exe라는 프로세스가 존재하는지 아닌지 검사를 하는 구간인데요.
실제로 게임 보안 프로그램들의 이런 구간들이 항시 돌면서 계속 검사를 하는 편입니다.
그러면 이 부분이 왜 프로세스 탐지 부분인지 조금 짚어봅시다.
이 부분에서 살짝 위로 올리다 보면 다음과 같은 곳을 발견하실 겁니다.
이렇게 API 함수 2개를 발견할 수 있습니다.
CreateToolhelp32Snapshot와 Process32FirstW의 경우 현재 실행되는 프로세스들의 목록을
얻기 위함으로 매우 자주 쓰이는 함수들입니다. 기억해 두시면 좋습니다.
이 프로그램이나 실제 보안 프로그램들이 사용하는 API 함수들은 공통적일 수밖에 없으니까요.
그리고 밑을 보면 다음과 같습니다.
즉 이걸 풀어서 보자면
1. 실행되는 프로세스들의 이름을 얻는다.
2. ollydbg.exe라는 프로세스의 이름이 있는지 체크한다.
3. 있다면 OpenProcess API를 이용해 그 프로세스의 핸들을 얻어낸다.
4. GetExitCodeProcess API를 이용해 프로세스의 종료 상황을 얻어낸다.
5. TerminateProcess API를 이용해 프로세스를 종료시킨다.
이 방어 로직이 돌아가고 있는 프로그램입니다.
근데 저희는 Cheat Engine으로 쳐다보고 있기 때문에 해당 사항은 없죠.
하지만 알아둬야 하는 이유는 이 ollydbg가 언제든지 다른 것으로 바뀌지 말라는
법은 없고 결국 실제 보안 프로그램에선 Cheat Engine도 포함을 하기 때문에
우회를 하려면 알아둬야 한다는 겁니다.
다시 돌아와서 해결법으로
기본적인 방어 로직이 있다는 거는 확인했습니다.
그러면 원래대로 프로그램으로 돌아와서 해결 방법을 찾아야겠죠.
그냥 우선 Check the value를 눌러봤습니다.
그랬더니 WrongPass.. Try Again !이라는 문구가 떴습니다.
자연스레 문구를 추적해 봅시다.
이 프로그램의 경우 UTF-16을 체크해 주셔야 문구가 뜹니다.
이걸 어셈블리 스캔으로 할 경우 따로 접근하는 명령어는 없습니다.
이 경우에는 레지스터에 넣어서 처리하고 있는 거라 Data BreakPoint로 하시면
어디서 접근하는지 찾을 수 있습니다.
우선 이렇게 문구에 접근하는 부분을 찾았습니다.
일단 밑에 DispatchMessageW 이 있는 걸 보면 문구를 띄우는 곳은 맞는데
밑의 리턴 주소들을 하나씩 살펴봐도 정답 조건으로 보일만한 곳들이 전혀 안 보입니다.
이러면 이 문구 쪽은 조건이랑은 별 상관없이 문구만 출력해 주는 곳인 거고
정답 조건은 아예 다른 곳에서 처리를 한다고 봐야 할 거 같습니다.
여태 하던 메시지 추적이 실패했지만 다른 방법으로
그러면 위의 첫 번째 Bar 숫자를 스캔해 보죠.
이렇게 2개가 떴는데 2개 다 디버깅을 한 번 걸어보겠습니다.
Find out accesses로 걸었습니다.
자 이렇게 디버깅 결과들이 떴는데, 빨간색 박스의 명령어를 제외한 나머지들은 전부
숫자를 올리고 내리는 등의 행위들에서만 카운팅 됐습니다. 빨간색 박스는 제가 Check를
눌렀을 때 카운팅 되는 걸 봐서 이 숫자가 정답인지 아닌지를 읽어야 하니까 그 이유로
접근하는 명령어가 아닐까 하는 의심이 됩니다.
자 그러면 이렇게 빨간색 박스를 쳐둔 명령어에다가 디버깅을 걸고 체크를 눌렀을 때 모습입니다.
Return Address들을 쭉 보면 SendMessageW가 보이실 겁니다. 어떤 메시지를 보내는 거죠.
이 숫자 관련 메시지를 보낸다고 합리적인 의심을 해볼 수 있을 거 같습니다.
그러면 그 밑 부분인 1B15를 한 번 보실게요.
166DF71D4B6E8A6371.exe+1B15 - 8B 8E 40010000 - mov ecx,[esi+00000140] < 이곳
166DF71D4B6E8A6371.exe+1B1B - 6A 00 - push 00 { 0 }
166DF71D4B6E8A6371.exe+1B1D - 6A 00 - push 00 { 0 }
166DF71D4B6E8A6371.exe+1B1F - 68 00040000 - push 00000400 { 1024 }
166DF71D4B6E8A6371.exe+1B24 - 51 - push ecx
166DF71D4B6E8A6371.exe+1B25 - 8B D8 - mov ebx,eax
166DF71D4B6E8A6371.exe+1B27 - FF D7 - call edi
166DF71D4B6E8A6371.exe+1B29 - 8B 96 94010000 - mov edx,[esi+00000194]
166DF71D4B6E8A6371.exe+1B2F - 6A 00 - push 00 { 0 }
166DF71D4B6E8A6371.exe+1B31 - 6A 00 - push 00 { 0 }
166DF71D4B6E8A6371.exe+1B33 - 68 00040000 - push 00000400 { 1024 }
166DF71D4B6E8A6371.exe+1B38 - 52 - push edx
166DF71D4B6E8A6371.exe+1B39 - 8B E8 - mov ebp,eax
166DF71D4B6E8A6371.exe+1B3B - FF D7 - call edi
166DF71D4B6E8A6371.exe+1B3D - 0FAF C5 - imul eax,ebp
166DF71D4B6E8A6371.exe+1B40 - 0FAF C3 - imul eax,ebx
166DF71D4B6E8A6371.exe+1B43 - 3D 776B0000 - cmp eax,00006B77 { 27511 }
166DF71D4B6E8A6371.exe+1B48 - 0F85 25010000 - jne 166DF71D4B6E8A6371.exe+1C73
밑 부분 보니까 조건문이 달려 있습니다.
eax에 현재 5C가 담겨있고 밑에서 mov ebx, eax를 통해 ebx에 5C를 그대로 옮겨줍니다.
5C=92 // 현재 제가 1번째 Bar에 설정해 둔 수치랑 동일
즉 Bar에 담긴 숫자 검사인게 맞는 듯하네요.
우선 조건은 eax * ebx 했을 때 6B77이 나오면 조건에 성립하는 거 같습니다.
그러면 ebx는 첫 번째 Bar 숫자인 게 확인이 됐으니 eax가 어디서 나오는지를 알아야 할 거 같네요.
그러려면 위의 명령어들을 조금 해석을 해보면 이렇습니다.
166DF71D4B6E8A6371.exe+1B13 - FF D7 - call edi < SendMessage
166DF71D4B6E8A6371.exe+1B15 - 8B 8E 40010000 - mov ecx,[esi+00000140]
166DF71D4B6E8A6371.exe+1B1B - 6A 00 - push 00 { 0 }
166DF71D4B6E8A6371.exe+1B1D - 6A 00 - push 00 { 0 }
166DF71D4B6E8A6371.exe+1B1F - 68 00040000 - push 00000400 { 1024 }
166DF71D4B6E8A6371.exe+1B24 - 51 - push ecx
166DF71D4B6E8A6371.exe+1B25 - 8B D8 - mov ebx,eax
166DF71D4B6E8A6371.exe+1B27 - FF D7 - call edi < SendMessage
166DF71D4B6E8A6371.exe+1B29 - 8B 96 94010000 - mov edx,[esi+00000194]
166DF71D4B6E8A6371.exe+1B2F - 6A 00 - push 00 { 0 }
166DF71D4B6E8A6371.exe+1B31 - 6A 00 - push 00 { 0 }
166DF71D4B6E8A6371.exe+1B33 - 68 00040000 - push 00000400 { 1024 }
166DF71D4B6E8A6371.exe+1B38 - 52 - push edx
166DF71D4B6E8A6371.exe+1B39 - 8B E8 - mov ebp,eax
166DF71D4B6E8A6371.exe+1B3B - FF D7 - call edi < SendMessage
우선 call edi는 전부 SendMessage를 호출합니다.
call edi 이후 eax에 리턴 되는 값들을 자세히 보면 제가 써놓은
1번째 92, 2번째 26, 3번째 67 임을 알 수 있습니다.
166DF71D4B6E8A6371.exe+1B3D - 0FAF C5 - imul eax,ebp
166DF71D4B6E8A6371.exe+1B40 - 0FAF C3 - imul eax,ebx
즉 여기서 처음 eax, ebp는 2번째 값과 3번째 값인 26과 67을 곱하는 거고
둘째 eax, ebx는 아까 ebx가 1번째 값인 92=5C가 담겨있다 했죠.
그러면 1번째 값 x 2번째 값 x 3번째 값 => 92 x 26 x 67을 의미합니다.
그러면 이 3개의 곱이 정확하게 6B77, 27511이 되어야 한다는 얘기죠..
3개의 숫자를 곱해서 1이 나오려면 우선 무조건 1의 자리는 1이어야 합니다.
27511이 숫자가 큰 편은 아닌지라 계산기로 노가다를 좀 해보면 생각보다
금방 나오긴 할 겁니다.
답은 11 , 41 , 61 세 숫자를 곱하면 27511이 나올 겁니다.
옛날 프로그램이라 글자가 깨지는 건지 모르겠는데 아무튼 틀렸다고 하는 게 아니라
다른 문구 띄우는 거 봐선 정답이라 봐도 무방할 듯합니다.
프로그램도 올려놓을 테니 한 번 실습해 보시면 좋을 거 같습니다.
문구만으로 접근하는 게 만능은 아니라는 걸 잘 보여주는 사례인 듯합니다.
'Reversing & Cheat Engine' 카테고리의 다른 글
14. 크랙미 다섯 번째 (0) | 2024.02.27 |
---|---|
13. 크랙미 네 번째 (파도콘) (0) | 2024.02.26 |
11. 크랙미 두 번째 (0) | 2024.02.25 |
10. 리버싱의 꽃 후킹(Hooking) (0) | 2024.02.24 |
9. 크랙미 첫 번째 (0) | 2024.02.23 |