• [Flare-on 2019] Snake

    2020. 1. 21.

    by. ugonfor

    IDA로 nes파일 로드하는 게 없다.

    nesldr 라는 파일만 git에 있으나, 작동하지 않음

    한참동안 고민하다가 binary ninja도 사용해보고 ghidra도 사용해보고 hopper disassemble...까진 안감^^....

    아마 nes.ldw파일이 .dll로 loader형식이 바뀌면서 인식을 못하는 듯...

    ida 6.x를 설치하면 될 것 같기도 함


    Solution

    준비물:

    iNES loader for IDA 7.x (Thanks to @hhro hhro )

    lab313ru/inesldr

    NES emulator [Mesen.exe]

    Mesen.zip
    8.46MB

    풀이

    먼저 문제의 목적이 무엇인지 알아야 함.

    snake.nes를 열어서 플레이 해보면, 이 문제는 단순한 지렁이 게임이다. 사과를 먹으면 꼬리가 길어진다.

    아마도 사과를 무수히 많이 먹으면 게임이 클리어할 거라고 생각하고 시작.

    snake.nes를 IDA를 통해서 열어보면 다음과 같이 처음보는 어셈블리어를 볼 수 있음.

    인터넷에 찾아보니 6502라는 프로세서의 어셈블리어 였다...

    다음페이지에서 각 어셈블리어가 무슨 역할을 하는 지 알아볼 수 있다.

    6502.org: Tutorials and Aids

    Mesen을 이용해서 각 함수의 시작주소에다가 브포를 걸어서 각 함수가 어떤 상태에서 들어가게 되는 지 확인해봤다.

    화면 프레임을 바꾸어주는 함수, 꼬리의 위치를 옮겨주는 함수, 머리위치 옮겨주는 함수, 사과 위치 바꾸어 주는 함수, 그리고 죽었을 때 실행되는 함수 총 5개가 RESET과 NMI말고도 따로 코딩이 되어있었다.

    각 함수에 대해서 다 조사를 하고 난 후, 처음에 들었던 생각은 사과를 먹을 때마다 꼬리가 길어지니까, tail_location이 먹은 사과의 개수랑 관련이 있을 것 같아서 열심히 보았는 데, 잘 찾지 못하였다.

    그다음 본 것은 apple_location함수이다. 처음에는 이 함수가 apple_eat함수인줄 알았다. 그래서 열심히 관찰을 하였으나, 이 함수를 통과해도 사과의 위치는 바뀌지만 꼬리가 길어지지는 않았다.

    그래서 apple location 함수의 주변을 보았는 데, 다음과 같이 되어있었다.

    (break, apple_Y 등 이런 이름들은 처음에는 전혀 없었음. 내가 다 이름 지어준거임)

    6502 어셈블리에서 가장 곤란했던 것이 레지스터가 없던 것이다. 분명히 cmp 어셈블리가 있어서 무언가를 비교할 텐데, 뭐랑 뭐를 비교하는 지 잘 몰라서 좀 어려웠다.

    하여튼, apple_location 함수가 있는 박스의 위쪽(loc_C3CE)에서 BNE를 무조건 아래로 가게 하였지만, 사과의 위치만 바뀔 뿐, 꼬리가 길어지지는 않았다.

    그러나, 적어도 loc_C3CE에서 사과를 먹었는 지 확인하는 과정은 있을 거라 생각했다. 그리고 그 위에 byte_xx(사진에서는 break) 있었고, 그 값이 사과를 먹는 것과 연관이 있을 것이라 생각하고 디버깅을 해보았다.

    그 결과, break의 경우에는 벽이나 사과에 부딪히는 경우 값이 변해서 BNE에서 loc_C3D8로 가게 된 것이었다.

    즉, 저 부분은 사과를 먹거나, 뱀이 죽으면 사과의 장소가 이동되어야 하기 때문에 있는 부분임.

    그래서 break값이 1이라는 것은 어딘가에 부딪힌 것이고, 0이라는 것은 부딪히지 않은 것이라는 것을 알았다.

    이후 찾은 것이 이 부분이다. 결국에는 사과를 먹었을 때, break값을 변화시켜주어야 하기 때문에 cross reference를 찾아보았고, 각 부분에 break를 걸어서 확인을 해보았는 데, 현재 내가 eat_apple 이라는 부분을 지날 때마다 뱀의 꼬리가 길어지는 것을 알 수 있었다.

    그래서 그 부분을 기준으로 거꾸로 돌아가 보았고, loc_c811에서 사과의 좌표랑 뱀 머리의 좌표를 비교하는 것을 알 수 있었다.

    MESEN의 디버깅 기능을 이용하면 다음처럼 어셈블리를 볼 수 있는 데, 각 메모리의 값도 알 수 있어서, 어떤 것이 뱀 머리의 좌표이고, 어떤것이 사과의 좌표인지 알 수 있었다. 이를 통해서 위의 사진에서 이름도 정해준 것이다.(계속 변하는 값이 head_X,Y, 사과 먹을때만 변하는 것이 apple_X,Y 임)

    그래서 아래 사진의 부분에서 사과와 뱀 머리의 좌표를 비교해서 일치하면 사과 먹는 것으로 처리하는 것을 알았고, BNE가 아니라 BEQ로 패치해줘서 사과와 X,Y좌표가 다르면 사과를 먹는 것으로 처리하게 하였다.

    그랬더니, 사과를 일정개수이상 먹으면 다음라운드로 진행되었고, 각 라운드는 속도가 빨라졌으며, 4라운드까지 넘어가면 flag가 나왔다.

    CLEAR!

    Flag: NARPAS-SWORD@FLARE-ON.COM


    처음에 풀었을 때는 위처럼 풀었으나, 몇 차례 메모리 뷰와 함께 보았더니, byte_25가 현재 먹은 사과의 개수 였던 것을 알 수 있었고, 그 아래 왼쪽 박스에 잇는 byte_xx(아래 사진에서는 이미 round라고 이름 바꿈)가 round라는 것을 알 수 있었다.

    그래서 단순히 cmp값을 33이 아니라 0으로 바꾸거나, BNE eat_apple을 BEQ eat_apple로 바꾸었어도 금방 풀수 있었을 것 같다.

    어셈블리를 이해하는 게 더 어려웠던 문제임...

    32비트나 64비트로 비유하면 문제 자체는 jmp문 패치만 하면 되는 쉬운 문제였음...


    BNE는 Branch if Not Equal 이고, BEQ는 Branch if EQual이다. 그리고 따로 레지스터가 없이 메모리에 값을 적어서 매개변수로 사용함. ㅇㅇ..

    정확히는 모르겠는 데, LDA, STA가 대애충, 메모리 가져오거나, 메모리에 값을 쓰거나 하는 어셈블리인듯...

    LDA: LoaD Accumulator

    STA: STore Accumulator

    이름 다 보기 쉽게 바꾼 .idb파일 첨부함

    snake.nes.idb
    0.42MB

    'Writeup > Wargame_Writeup' 카테고리의 다른 글

    HackCTF Unexploitable #2  (0) 2020.01.23
    [Flare-on 2019] Reloadered  (0) 2020.01.21
    [Flare-on 2019] Demo  (0) 2020.01.21
    [Flare-on 2019] DnsChess  (0) 2020.01.21
    [pwnable.kr] flag  (0) 2020.01.12

    댓글