본문 바로가기

2. Reversing (리버싱)/1) Write UP

[2020.05.14] CSAW365 | Gametime

CTF문제를 풀어보며, 새로운 CTF 사이트를 접하게 되었다

정말 내가 풀 수 있을 만한 난이도로, 완전 기초부터 적당한 난이도였고

이제 답지를 보지 않고 스스로 해결해 보고 싶어서 처음 시작하게 되었다!

웹, 크립토, 리버싱, 포너블, misc 등 분야별로 문제가 있는데

분야별로 문제 수도 굉장히 고르게, 난이도도 고르게 많아서 좋은 것 같다

이번 문제는 1번 이기 때문에

그다지 어렵지 않을거라 예상 했지만, 답지를 안보고 푸는 것이라

다른 의미로 고민을 많이 했던 것 같다. 문제 자체는 쉽다:D

문제는 위와 같다

Gametime이라는 문제인데, 제목에서처럼 프로그램을 실행하면 Game이 나온다

그런데 여기서 참고사항은 The flag is not in the flag{} format이다.

흔히 CTF의 플래그들 처럼, flag{}형태는 아니라는 것이다

문제를 잘 읽는게 정말 중요한 것 같은게,

진짜 들어가서 처음에는 flag{}형태를 찾고 있었다ㅋㅋㅋㅋ

쨋든 먼저, 프로그램을 실행하여

어떤 프로그램인지 확인해보도록 하자

처음 실행하면 위와 같이 R U Readdy?!라는 문구가 표시 되고

카운트다운이 시작된다

그리고 위와 같이 이어서 s가 뜰때 스페이스바를 누르고

x가 뜰때 x키를 누르라고 한다

이어서 m도 뜬다. 이것 또한 .....m 이라는 글씨가 뜰 때 m 키를 눌러주면 된다

이 게임에서 모든 것을 잘못누르거나 특정시간 내에 누르지 않게 되면 꺼지게 된다

그래서 일단 끝까지 가기 위해서는 이 게임을 모두 성공해야한다

TRAINING COMPLETE! 이후

Now you know everything you need to know..

for the rest of your life!가 뜬다

이후, LETS PLAY!라고 뜨는데

위에는 연습문제였던 것이었다.(그렇지만 연습 문제에서도 틀리면 꺼지게 된다)

이제 본격적인 질문들이 나오게된다

연습 게임과 차이점은, 문제가 쉴새 없이 쏟아지고 기다려주는 텀이 짧아진 듯한 느낌이다

LETS PLAY 이후

카운트다운이 시작되며 저렇게 s,x,m의 질문 공세가 쏟아진다

여기까지 성공적으로 했을 때,

"ooooh, you fancy!!!" 와

"key is not (NIIIICE JOB)!!!" 가 뜬다

중간에 갑자기 key 값이 뜬다

답이 이것이다

no5c30416d6cf52638460377995c6a8cf5

아니 이게 끝이라고???

뒤에 이후 문제를 계속 풀 수 있긴 한데

x,m,s문제의 무한 반복이며 틀리면 창이 닫히는 열받음을 감수해야 한다

어쨋든 이렇게 답을 구했다

그렇지만, 이런 게임으로 답을 구하는건 뭔가 찜찜하다

우리는 리버싱을 하러 온 것이지 게임을 하러온건 아니기 때문이다

여기까지는 프로그램이 어떻게 동작하는지를 살펴 보았다고 보면 된다

이제 올리디버거 툴을 사용하여 확인을 해보고자 한다

한쪽에는 이렇게 올리디버거를

한쪽에는 프로그램 실행창을 띄워주었다

한줄씩 내려자보자

F8을 눌러 한줄 씩 실행 시

00CD15BF에서 다음과 같은 첫 실행이 이루어짐을 알 수 있다

아래의 사진에서 오른쪽에 ZOMGZOMG박스 부분이

엄청나게 광범위한 레지스터 함수들로 구성되어있는 것을 확인할 수 있다

올리디버거로 한줄 씩 확인해 보면,

우리가 처음에 게임을 실행 했을 때, 너무 빠르게 지나가서

눈에보이지 않았던 key is not () 과 같은 문자열도 확인이 가능하다

짝을 짓자면 이런 식으로 연결이 된다

여기서 BP를 걸어둔 대부분이 저런 문자열들을 출력하고

변화가 생기는 지점으로 보면 된다

이런식으로 특정 CALL 등을 지나면 문자열이 출력된다

무수한 CALL들을 지나

한가지 특징을 발견하게 되었다

그냥 프로그램 실행 시, 기존에는 틀리면 그냥 종료되는 것을 볼 수 있었는데

올리디버거로 확인해보니 답이 틀리는 경우에

UDDER FAILURE! 가 뜨는 것을 확인할 수 있었다

CALL함수...s를 실행 시키고

ADD ESP,10을 지나면 Udder FALIURE가 뜨게 된다

그리고 바로 밑의

JE에서 점프를 하는지 마는지가 결정되는 것을 볼 수 있다

전에도 정리했었는데,

 

[리버싱] 조건 분기 명령어와 코드 수정 지점

JE Jump if Equal결과가 같으면 (결과가 같아서 1, ZF가 0이면)점프​JA Jump if it's above...

blog.naver.com

여기에서 볼 수 있듯

JEJump if Equal

결과가 같으면 (결과가 같아서 1, ZF가 0이면)점프되는 것으로 이해할 수 있다

그래서 이런 원리에 의해

이렇게 된 ZF 1을

ZF 0으로 고쳐주면 점프가 비활성화 된 것을 확인할 수 있다

참고로 점프를 고쳐주지 않으면 다른 함수로 빠져서 최종적으로 막다른 RETN으로 가서

프로그램이 종료되는 것을 확인할 수 있었다

밑은 거의 이러한 비슷한 패턴이 반복된다

위의 패턴이 끝나고 F8을 눌러 조금만 밑으로 내려와도

다음과 같은 내용을 또 확인할 수 있다

CALL을 통해 문자열을 불러오고

ADD를 통해 에러 메세지를 띄우는 것을 볼 수 있다

이 또한 분기점에 의해 점프문이 활성화 되어 있는 것을 볼 수 있다

위와 정확히 같은 원리이므로

다시 ZF 1을 0으로 바꿔주어서

위와 같이 함수를 무력화 시켜주었다

이 과정으로 함수 안에 들어올 수 있는 것을 확인하였다

지루하지만 이 과정을 3번정도 해주었던 것 같다

이후

00CD170F를 지나면

PUSH된 내용을 바탕으로 00CD1719의 MOV함수를 통해서

TRAINING COMPLETE!를 출력하는 것을 확인할 수 있다

그러던 중 눈에 띄는 부분이 표시 되었다

00CD173부분을 보면 JNZ를 통해 00CD1725로 돌아가는 것을 볼 수 있다

위의 링크

 

[리버싱] 조건 분기 명령어와 코드 수정 지점

JE Jump if Equal결과가 같으면 (결과가 같아서 1, ZF가 0이면)점프​JA Jump if it's above...

blog.naver.com

에서 보았던 것 처럼,

JNZJump if it's not Zero라는 의미로

결과가 0이 아니면(결과가 1이면, 즉 ZF가 0이면) 점프

ZF가 1이면 점프하지 않고 다음 명령 실행 하게 된다

JNZ가 활성화되어있으면 저 곳이 무한 반복 되기 때문에

이 부분을 해제될 필요성을 느꼈다

ZF를 1로 고쳐주었고

조금 더 내려와

Now you know everything you need to know를 출력하는 PUSH문을 확인하였다

그러나 이때, 또 JNZ 반복문이 보인다

그래서 같은 작업을 또 해주었다 (ZF를 1로 수정)

여기도 다음으로 넘어가려면 같은 낌새를 보인다

같은 구조들이 반복되니

레지스트리 구조도 비슷한 것이 당연하다

여기까지 힘들게 왔는데

프로그램은 key가 NIIICE JOB이 아니라는 농락도 한번 해준다

열이 받는다

그러나 갑자기 새로운 변화가 생겼다

JL을 발견하였다!

JL을 통해 반복문이 계속 되는 지점은 어떻게 수정해 주어야할까?

JLJump A lesser than B이고

~보다 작으면 주소 A로 점프하라 라는 의미이다

JL의 경우에는 Flag를 통해 수정할 수 없어서,

나의 경우 Assemble을 통해

JG로 수정해주었다

JG는 참고로 Jump A greater than B

~보다 크면 ~로 점프하라 이다

JG를 통해 저 주소로 가지 않고

다음 명령어를 실행하는 것으로 이해하였다

문자열 TURBO TIME 이 출력되었다

이 문자열을 기억해야한다

이 문자열을 지나 수십줄 밑으로 내려와서,

이 부분을 만났다

그러면 당연히 어떤 생각이 드는가?

JNZ가 있는 한 또 무한반복이 될 것이기 때문에

플래그를 1로 고쳐주면 된다고 생각할 것이다

이렇게 또 함수를 탈출하여

JL지점에서 실험을 해보았다. 위에서는 JG로 바꿔주었는데,

한번 NOP도 채워봤다

정상적으로 밀리는 것 보니 이 방법도 괜찮은 것 같다

그렇지만 이 실습 중

JNZ였나 이 경우를 플래그를 안바꾸고 NOP로 채워준 경우에는

반복문이 살아나는 경험도 했었던 것 같다

모든 플래그와 반복문을 피해

끝까지 왔더니 YOU DID IT!!!을 출력한다

야호!!!!!!!!

읭?? 근데 뭔가 이상하다

끝은 봐서 기쁘지만.....

우리는... 끝내면 안되는데????? key를 찾아야하는데??????

여기서 잠깐!!! 혹시

TURBO TIME을 기억하는가?

이 부분을 반복할 때마다 %02x, 즉 16진수 대문자열을 받아와 주는 것이었다

 

[리버싱] Format Specifier

printf() / sprintf() / fprintf() 함수 속의 퍼센트(%) 기호들은, "Format Specifier" 라고 ...

blog.naver.com

그래서 이 부분은 탈출 하면 안되는 구간이었다

이 문자열을 반복해주면,

반복할 때마다, 그리고 CALL함수를 지날 때마다

문자열이 늘어나는 것을 확인할 수 있다

이게 포인트였다고 생각한다

함수를 정확히 이해하고 어떤 행동을 취해야 하는가를

알아야 풀 수 있는 문제였다고 생각한다

계속 반복을 해준다

짠!

물론 보는 것과 같이

저렇게 문자열을 다 출력해도 반복문을 탈출하긴 한다

이렇게 쌩고생을 한건...

디버깅을 하기 전에 위에 실습만으로 사실 key를 확인 했었던 것 기억하는가

그 지점을 알고 TURBO TIME뒤를 유심히 보았다면 프로그램이 종료되지 않았겠지만,

나의 경우는 첫 프로그램 실행 당시

사실 저 key가 정답인지 모르고 있었다ㅋㅋㅋㅋ

그래서 이 노가다를 하다가 원인을 찾아낸것...!

그래도 간단한 프로그램이지만

정말 시간이 오래걸리고 초보자들에게 큰 도움이 되는 실습이었다고 생각한다