

나랏말싸미… 듕귁에달아…
리버싱부터 시작하겠다.
main 부터 보면,


Flag를 출력해주는 부분이 있고, 사용 방법 등 기본 역할을 하는 부분을 확인할 수 있다.
저기서 Game::run 함수 부분을 확인해보겠다.



나랏말싸미 악용하기에서는 입력으로 map을 줄 수 있다.
따라서, run에서 사용할 수 있는 취약점을 찾아야한다.
arr1 이 스택이고, arr[10000]이 스택 카운터라는 것을 알 수 있다.
분석해보면, 해당 vm의 동작 방식은 다음과 같다.
| 초성 | case 값 | 행동 |
|---|---|---|
| ㄱ | 0 | stack.pop() |
| ㄲ | 1 | stack.push([현재 칸의 글자의 종성값]) |
| ㅁ | 6 | 덧셈 연산. 현재 칸의 글자에 종성이 있을 경우: stack[-1] += 종성 / 종성이 없을 경우: stack[-2] = stack[-1] + stack[-2]; stack.pop() |
| ㅂ | 7 | 뺄셈 연산. 덧셈 연산과 동일한 방식으로 동작. |
| ㅃ | 8 | 곱셈 연산. 덧셈 연산과 동일한 방식으로 동작. |
| ㅅ | 9 | 나눗셈 연산. 덧셈 연산과 동일한 방식으로 동작. |
| ㅆ | 10 | 나머지 연산. 덧셈 연산과 동일한 방식으로 동작. |
| ㅐ | 11 | No-op. |
| ㅈ | 12 | Load 연산. value = map[stack[-1]][stack[-2]] 의 종성; stack.pop(); stack.pop(); stack.push(value); |
| ㅉ | 13 | Save 연산. map[stack[-2]][stack[-3]]의 종성 = stack[-1]; stack.pop(); stack.pop(); stack.pop(); |
| ㅋ | 15 | Conditional branch 연산. stack[-1] >= stack[-2] 을 만족하지 못했을 때 원래 향할 방향의 반대로 이동. |
| ㅌ | 16 | Conditional branch 연산. stack[-1] == stack[-2] 을 만족하지 못했을 때 원래 향할 방향의 반대로 이동. |
| ㅍ | 17 | exit([현재 칸의 글자의 종성값]) |
| ㅎ | 18 | stack.push(input[input_ptr++]) |
모음이 ㅗ, ㅜ, ㅓ, ㅏ일 경우 각각 상, 하, 좌, 우에 있는 칸으로 움직인다.