-
[reversing.kr] Position 풀이Wargame/reversing.kr 2021. 5. 22. 20:14
1. 프로그램 분석
첨부파일로 Position.exe와 ReadMe.txt 파일이 있다. 우선 Position.exe를 실행시켜보자.
특정 Name과 Serial을 입력해야 하는 듯하다. ReadMe.txt 파일을 열어보자.
파일에 대한 설명이 나와있다. Serial이 76876-77776일때의 이름을 찾으면 된다. 이름은 네 글자이고 'p'로 끝난다고 한다.
2. Debugging(with x32dbg & Ghidra)
x32dbg로 파일을 열어보자. 사용되는 모듈을 먼저 보면 mfc로 작성된 프로그램임을 추측해볼 수 있다. 디버깅이 쉽지는 않을 것 같다. 어떻게 접근해볼지 고민하다가 Wrong이라는 문자열이 있는 것을 보아 적절한 input에 대해 Correct와 같은 문자열이 출력될 것이라 생각했다. 문자열 참조를 살펴보자.
다행히 Correct!라는 문자열이 있다. 해당 문자열이 참조되는 부분으로 이동해보자.
위와 같이 동그라미 친 부분에서 함수가 호출되고, test eax, eax 결과에 따라 Correct 문자열 혹은 Wrong 문자열이 출력된다. Name으로 "abcp"(임의의 조건을 만족할 가능성이 있는 이름), Serial로 "76876-77776"로 input을 주고, 해당 함수 내부로 들어가 보자.
#7006 함수가 호출된 후, Name 길이가 ecx-C의 위치에 담긴다. 007D17A6주소의 je 명령어에서 점프가 일어나지 않으면 더 이상의 검사가 일어나지 않고 return 한다. 기본적으로 Name은 길이가 4여야 함을 알 수 있다.
Name에 대해 각 문자가 'a'~'z' 범위를 만족하는지 검사하는 루틴이다. Name이 각각 알파벳 소문자로 이루어져 있음을 알 수 있다.
Name에 대해 각 문자가 서로 다른 문자인지 검사하는 루틴이다.
본격적으로 Name으로부터 serial을 생성하고, 입력한 serial과 비교하는 루틴이 시작된다. 처음에는 일일이 어셈블리 코드를 따라가면서 디버깅을 해보려고 했는데, 양이 많고 특정 패턴이 반복되는 것 같았다. Ghidra의 디컴파일 뷰를 통해 코드를 살펴보기로 하자.
mov cl, al 이후의 디컴파일 뷰 중 일부를 발췌해봤다. 해당 부분에 Name을 문자열 배열이라 생각하고 해석하면 (Name[1] >> 2 & 1) + 1 + (Name[0] & 1) + 5)와 Serial[0] 값을 비교해서 같아야 한다. 다른 부분에 대한 검사도 어느 정도 shift 하는지에 대한 값이 다를 뿐, 큰 틀은 똑같다.
3. Solving(with python3)
분석한 내용을 바탕으로 다음과 같은 파이썬 코드를 작성하였다.
name = ['x', 'x', 'x', ord('p')] ans = [7, 6, 8, 7, 6, 7, 7, 7, 7, 6] for i in range(ord('a'), ord('z')+1): for j in range(ord('a'), ord('z')+1): for k in range(ord('a'), ord('z')+1): if i!=j and j!=k and k!=i: name[0] = i name[1] = j name[2] = k tmp_ans = [0 for i in range(10)] tmp_ans[0] = ((name[1]>>2)&1)+1 + ((name[0]>>0)&1)+5 tmp_ans[1] = ((name[1]>>3)&1)+1 + ((name[0]>>3)&1)+5 tmp_ans[2] = ((name[1]>>4)&1)+1 + ((name[0]>>1)&1)+5 tmp_ans[3] = ((name[1]>>0)&1)+1 + ((name[0]>>2)&1)+5 tmp_ans[4] = ((name[1]>>1)&1)+1 + ((name[0]>>4)&1)+5 tmp_ans[5] = ((name[3]>>2)&1)+1 + ((name[2]>>0)&1)+5 tmp_ans[6] = ((name[3]>>3)&1)+1 + ((name[2]>>3)&1)+5 tmp_ans[7] = ((name[3]>>4)&1)+1 + ((name[2]>>1)&1)+5 tmp_ans[8] = ((name[3]>>0)&1)+1 + ((name[2]>>2)&1)+5 tmp_ans[9] = ((name[3]>>1)&1)+1 + ((name[2]>>4)&1)+5 #print(tmp_ans) if ans==tmp_ans: for ch in name: print(chr(ch), end="") print()
위 스크립트를 실행하면 flag Name에 대한 값이 여러 개 나타난다. ReadMe.txt에서도 답이 여러 개 있을 수 있다고 하는데, 역시 각각을 입력 값으로 주면 아래와 같이 Correct 문자열이 나타나는 것을 확인할 수 있다.
4. Comment
풀이 과정 자체는 단순하지만, 문제를 푸는데 꽤 오랜 시간이 걸렸다. 일단 160점이라는 비교적 높은 문제 배점이라는 점, 어셈블리 코드가 길어져서 곧이곧대로 분석하는 게 맞을까 하는 의심이 들었다. 디컴파일 뷰를 보니 어느 정도 규칙성이 있었고 파이썬 코드 작성을 통해 간단히 flag를 획득할 수 있었다.
반응형'Wargame > reversing.kr' 카테고리의 다른 글
[reversing.kr] ImagePrc 풀이 (0) 2021.05.09 [reversing.kr] Replace 풀이 (0) 2021.03.09 [reversing.kr] Music Player 풀이 (0) 2021.02.09 [reversing.kr] ransomware 풀이 (0) 2021.01.08 [reversing.kr] Easy ELF 풀이 (0) 2021.01.05