반응형

 

Replace.exe
0.04MB

 


파악

 

exeinfo에 문제 파일을 올려보니 C++로 작성된 파일이다.

 

 

문제 파일을 실행해보면 위와 같다.

 

 

문자는 입력이 안되는데 숫자는 입력이 된다.

 

하지만 위와 같이 임의의 값으로 12345를 주면 "Wrong" 문자열이 뜬다.

 


분석

 

 

문제 파일을 x32dbg에서 열어보면 위와 같다.

 

 

x32dbg의 문자열 찾기 기능을 이용해보니 위와 같이 뜨고, "Correct!" 문자열이 보인다.

 

해당 문자열을 더블 클릭하여 이동한다.

 

 

그러면 위와 같이 401073 주소에서 "Correct!" 문자열을 스택에 넣고 이어서 40107E 주소의 GetDlgItemTextA() API 함수를 통해 출력하는 것 같은데, 문제는 401073 주소로 조건 분기하는 부분이 없다는 것이다.

 

"Correct!" 문자열을 스택에 넣기 전 40105A 주소에 GetDlgItemInt() API 함수가 있는데, 이 함수는 사용자가 입력한 값을 정수로 변환하여 가져오는 역할이므로, 아마 사용자가 입력한 후 Check 버튼을 누르면 40105A 주소에 있는 GetDlgItemInt() API 함수를 호출할 것이다.

 

그러므로 40105A 주소에 BP를 설치하고 F9 키를 눌러 실행한다.

 

 

그러면 입력 창이 위와 같이 뜰 것이고, 임의의 값 12345를 입력해준 후 Check 버튼을 누른다.

 

 

그러면 BP를 걸어둔 40105A 주소에 멈추게 되는데, F8 키를 눌러 GetDlgItemInt() API 함수를 호출해 입력한 값을 가져온다.

 

 

GetDlgItemInt() 함수의 반환값이 담긴 eax를 보면 3039가 담겨있다.

(10진수 12345가 16진수로 3039이다.)

 

그리고 이 3039 값은 4084D0 주소에 담기게 된다.

 

 

F8 키를 눌러 401060 주소의 명령을 수행하고 나면 401065 주소에서 40466F 주소의 함수를 호출한다.

 

F7 키를 눌러 40466F 주소에 있는 함수 내부로 들어간다.

 

 

그러면 40466F 주소에서 또 40467A 주소의 함수를 호출한다.

 

다시 한번 F7 키를 눌러 40467A 주소의 함수 내부로 들어간다.

 

 

40467A 주소에 있는 명령을 보면 619060EB 값을 406016 주소에 넣고, 404689 주소의 함수를 호출한다.

 

 

404689 주소의 함수는 404684 주소 바로 아래에 있는데, 4084D0 주소에 있는 값을 한 번 증가시키고 ret 명령을 수행한다.

 

근데 여기서 생각해봐야 하는 게 call 명령의 동작이다.

 

call 명령은 내부적으로 호출하는 함수가 끝나고 수행되어야 하는 다음 명령을 스택에 넣는다.

 

위의 사진을 보면 아직 404684 주소의 call 명령을 실행하기 전이고, 그 밑에 보이는 스택 사진은 call 명령을 실행하기 전의 스택 상태이다.

 

 

위 사진은 404684 주소의 call 명령을 실행하고, 404689 주소의 inc 명령까지 실행한 후 이제 40468F 주소에 있는 ret 명령을 실행하기 직전의 스택 상태이다.

 

ret 명령은 현재 스택에서 가장 위에 있는 값을 가져와 ip(eip, rip)가 해당 주소를 가리키게 하여 해당 주소로 점프한다.

(이해를 돕기 위해 예시를 들자면 pop ip 명령 수행 후 jmp ip 명령을 수행하는 순서)

 

현재 스택에는 404689 주소가 있기 때문에 결론적으로 현재 스택 상태에서 404689 주소의 함수는 4084D0 주소에 있는 값을 두 번 증가시키는 역할이다.

(4084D0 주소에는 사용자가 입력한 값을 어떤 작업을 거쳐 변화시킨 값이 들어있다.)

 

 

그리고는 404674 주소로 ret 명령에 의해 점프하게 되는데, 여기서는 4084D0 주소에 있는 값에 601605C7 값을 더한다.

(이전에 4084D0에 있는 값은 3039였는데, 404689 주소의 함수에서 2가 증가됐기에 303B가 됐고, 이 값에 601605C7을 더하니 60163602가 된다.)

 

그리고 이어서 eax 값을 증가시킨다.

(eax의 값은 이전에 3039로 설정된 후 어떠한 작업을 하지 않았기 때문에 여전히 3039인 상태에서 1을 증가시키는 것이다.)

 

그리고는 ch에 있는 1byte 값을 bl에 더한다.

 

이어서 pushad는 차례대로 EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI 레지스터의 값을 스택에 저장하고, popad는 스택에 있는 값을 차례대로 EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI 레지스터에 넣는다.

 

 

그리고는 404684 주소에서 404689 주소에 있는 함수를 호출한다.

 

 

이 부분은 위에서 설명했듯이 4084D0 주소에 있는 값을 증가시키는 것인데, 위의 사진에서와 같이 현재 스택에는 404689 주소가 쌓여있기 때문에 inc 명령이 두 번 실행되게 되어 4084D0의 값은 60163602에서 60163604가 된다.

 

 

그리고는 ret 명령에 의해 40106A 주소로 오게 되는데, 이 부분은 처음에 봤던 "Correct!" 문자열이 있던 부분이다.

 

 

40106C 주소에 의해 404690 주소로 점프하는데, 4084D0 주소에 있는 값 60163604을 eax에 옮긴 후 40469F 주소를 스택에 넣고 404689 주소에 있는 함수를 호출한다.

 

 

404689 주소에 있는 함수는 위에서도 언급했듯이 4084D0 주소에 있는 값을 증가시키는데, 이번에는 스택에 40469F 주소를 넣었기 때문에 한 번 증가시키고는 ret 명령에 의해 40469F 주소로 이동한다.

 

 

40469F 주소에서는 C39000C6 값을 40466F 주소로 옮긴다. 

 

그리고는 40466F 주소를 호출하는데, F7 키를 눌러 내부로 들어간다.

 

 

이전에 바로 위에서 40466F 주소에 C39000C6 값을 넣었는데 이 값은 eax 레지스터 값에 해당하는 주소에 90을 넣는다는 의미이다.

 

 

40466F 주소에서 F8 키를 눌러 실행하면 위와 같이 예외가 발생한다.

 

당연하다.

 

eax의 값은 60163604이고, 이 60163604 주소에 90이라는 값을 넣으려고 시도했는데, 이 60163604 라는 주소는 존재하지 않는 주소이기 때문이다.

 


정리

 

정리를 해보자면 

 

input value는 12345(=0x3039)이다.

 

input value : 12345

4084D0 = eax = 3039

401065 -> 40466F -> 40467A : 406016 주소에 619060EB를 넣고 

	→ 404689(1번째 호출) : 4084D0 주소에 있는 입력값을 2 증가후 404674 주소로 리턴
		- 404674 : 4084D0 주소에 있는 입력값에 601605C7를 더함 그러면 4084D0 주소의 값은 60163602가 됨
        		   , eax에 담긴 입력값에 1을 더함, bl에 담긴 값 1에 ch 값을 더함
                   
    → 404689(2번째 호출) : 4084D0의 값은 현재 60163602인데, 2를 증가시키기 때문에 60163604가 되고
    					 , 40106A 주소로 리턴

		- 40106C 주소에서 404690 주소로 점프, 404690 주소에서는 4084D0 주소에 있는 값 60163604를 eax로 옮기고
          , 40469F 주소를 스택에 넣은 후 404689 주소에 있는 함수를 호출하므로 4048D0 주소의 값은 60163605가 되고
          , eax는 60163604가 된다.

이후 40469F 주소에서는 c39000c6 값을 40466F 주소에 넣고, 40466F 주소를 호출하는데
40466F에서 eax 레지스터의 값 60163604에 해당하는 주소에 90을 넣으려고 하니 예외 발생

 

즉 "입력값 + 2 + 601605C7 + 2" 한 결과가 4084D0 주소에 담기는데, 이 값을 eax에도 넣는다.

 

여기서 eax 레지스터 값에 영향을 끼친다.

 


풀이

 

문제 분석에서 마지막 부분에 eax 레지스터의 값에 해당하는 주소에 90을 넣는 명령을 수행했다.

 

그렇다면 결국 eax 레지스터의 값이 중요하다는 것인데, 이 eax 레지스터의 값이 어떤 값이어야 할까

 

 

그건 위의 사진과 같이 40466F 주소에 C39000C6을 넣고, 40466F 주소의 함수를 호출하는 부분에서 알 수 있는데, 문제 분석에서는 eax에 말도 안되는 주소가 들어있었기 때문에 예외가 발생했었다.

 

하지만 만약 eax 레지스터에 유효한 주소가 들어있었다면, 40466F 주소의 함수가 끝나고

4046AE 주소에서 eax에 들어있는 값을 1 증가시키고, 다시 40466F 주소의 함수를 호출하여 증가된 eax 값에 해당하는 주소에 90을 넣고

4046AF 주소에서 6E8을 40466F 주소에 넣고, 이어서 pop과 mov 명령을 수행한 후 jmp 401071 명령을 실행하게 된다.

 

 

근데 여기서 401071은 이전에 "Correct!"  함수가 있던 부분이다.

 

그렇다면 eax에 401071이 들어가게 된다면, 40466F 주소의 함수에서 401071 주소에 0x90과 401072 주소에 0x90을 넣기 때문에 401073 주소의 "Correct!" 문자열을 스택에 넣는 명령이 실행될 것이다.

(opcode 0x90은 intel x86 아키텍처에서 NOP 명령을 의미한다.)

(https://ko.wikipedia.org/wiki/NOP_(%EC%BD%94%EB%93%9C))

 

시스템 해킹을 해봤거나 혹은 시스템에 대한 지식이 깊다면, NOP 명령의 역할을 잘 알 것이다.

 

NOP 명령은 아무것도 실행하지 않지만, 다음 줄의 명령으로 흐름이 흐르게 한다.

 

즉 401071 주소와 401072 주소에 NOP 명령이 있다면 IP가 401071로 이동됐을 때 NOP 명령으로 인해 401073 주소의 명령까지 실행 흐름을 흐르게 하고, 401073 주소에는 실행 가능한 명령이 있기 때문에 해당 명령을 실행한다.

 

 

즉 "입력값 + 2 + 601605C7 + 2" 한 결과가 4084D0 주소에 담기는데, 이 값을 eax에도 넣는다.

여기서 eax 레지스터 값에 영향을 끼친다.

 

 

이제 결국에는 eax 레지스터에 401071이라는 값이 들어가야 한다는 것을 알았는데, 어떻게 eax 레지스터에 401071 값을 넣을 수 있을까

 

그건 바로 이전에 "정리" 부분에서 정리했던 위와 같은 구문에서 알 수 있다.

 

"입력값 + 2 + 601605C7 + 2"의 결과가 eax 레지스터에 들어가게 된다.

 

근데 eax 값이 401071이어야 한다면, "입력값 + 2 + 601605C7 + 2"의 결과가 401071이어야 한다는 것이다.

 

이를 좀 다듬으면 "입력값 + 601605CB = 401071" 이다.

 

수식을 바꿔 "401071 - 601605CB = 입력해야 하는 값"이 된다.

 

 

401071 - 601605CB = A02A0AA6이고, 이는 10진수로 2687109798이다.

 

 

그래서 flag는 2687109798이 된다.

 

반응형

'전쟁 > reversing.kr' 카테고리의 다른 글

[reversing.kr] Music Player  (0) 2023.06.23
[reversing.kr] Easy_UnpackMe  (0) 2023.06.22
[reversing.kr] Easy_KeygenMe  (0) 2023.06.22
[reversing.kr] Easy Crack  (0) 2023.06.21

+ Recent posts