어제 같은 난이도 1단계를 풀다보니 똑같은 유형? 의 문제라서 그냥 넘겼다. 그래서 사실 세번째푸는거임!
오늘의 문제는 따단.
(링크 : https://crackmes.one/crackme/6655839ae7b35c09bb266464)
요거다 난이도는 1.5 나같은 초보에게 아주 적절하다. 그럼 문제 풀이를 시작해보자. 문제에는 패치가 필요하다고 나온다. 우선 참고만 해두자.
프로그램을 키면 이전과 비슷하게 패스워드를 입력하는 창이뜨고 리버싱을 통해 패스워드를 유추하거나 뭔가 패치를 통해 맞았습니다.가 뜨도록 해야하는 문제로 보인다.
아이다를 통해 해당 프로그램 메인 함수를 디컴파일 해주면 난이도 1.0 짜리 문제와 대충 유사한 모습을 확인할 수 있다. 대충 코드를 보면 반복문을 통해 패스워드를 입력하며 맞으면 Congraturation! 이 뜨도록 한다.
기존과 다르게 특정 함수를 불러와서 계산해서 if문을 처리하는 방식이 아니고 메인함수내에서 특정 로직과 계산을 통해 해당 값이 맞으면 정답으로 떨어지는 구조로 보인다. 그럼 if문을 통과하기 위해서는 어떤 부분을 조작 또는 계산해야 하는지 살펴 봅시다.
우선 v19 == 9 && !memcmp(v6, &buf2, 9uLL) 이라고 되어있습니다.
v19부분 부터 살펴보면 처음에 v19라는 변수가 선언되고 0으로 초기화 하는 부분을 제외하고 해당 함수를 할당하거나 별도 뭔가 하는게 없습니다. (그럼 이부분의 == 을 != 식으로 패치하면되는건가? 라고 생각)을 합니다.
패치를 해봅시다. 해당 부분을 명령어로 변경도 가능하지만. 그냥 바이트로 변경을 해봅시다. 변경이 필요한 부분은 아래와 같습니다. 해당 부분의 jnz 는 75 14 명령어 바이트를 가지고 있는데 jz로 변경합시다. 74 14
뭐 대충 이부분은 해결했다고 생각하고 다음 부분을 해결해봅시다.
!memcmp(v6, &buf2, 9uLL)
memcmp(p1, p2, len) 3가지 변수를 인자로 받는데 p1,p2는 포인터입니다! 해당 포인터 주소로 이동해서 len길이만큼 바이트를 비교합니다.
단순하게 p1,p2를 비교해서 같으면 0 / p1이 크면 양수 / p2가 크면 음수를 반환합니다.
우리가 통과해야하는if 문은 if( v19 == 9 && !memcmp(v6, &buf2, 9uLL) ) 니까 v6,&buf2를 같게 만들어서 0을 반환하면 통과할 수 있겠지요? 그러면 v6와 &buf2가 어떤 값인지 알아봅시다.
v6값은 cin 받은 값을 그대로 전달했으니 내가 입력한 값이 되겠군요.
그러면 &buf2 값은 어떤값일까요. buf2 값은 v3 값을 가져와서 WORD4 값을 51로 변경한 값입니다.
그러면 저게 어떤값인지 알아봅시당.WORD4는 상위4바이트를 의미하고 51은 hex로 변환 시 33으로 변경됩니당.
buf2값은 처음에 메모리에 이런식으로 들어갑니다.
47 30 43 72 40 63 6B 4D 00 00 00 00 00 00 00 00
여기서 상위 4바이트(WORD 4)가 33으로 변경되면 아래와 같습니다.
47 30 43 72 40 63 6B 4D 33 00 00 00 00 00 00 00
* 참고로 47 부터 작성된 이유는 리틀엔디언으로 메모리에 들어갈 때 저렇게 됩니다롱..
그러면 해당 hex 값을 string 으로 변환하면 아래와 같습니다.
G0Cr@ckM3 와우 딱봐도 패스워드 같이 생겼죠. 저걸 입력해봅시다.
오우.... 틀렸습니다... ㅋㅋㅋㅋㅋㅋㅋㅋㅋ 왜일까요. 이후에 코드 다시읽어보고 이리저리 테스트한 결과 똑같았습니다.
그런데 웃긴게 패치하지 않은 프로그램에 이전의 패스워드를 넣으니 성공이뜨더군요..!
그래서 이제는 디버깅을 해볼시간입니다. 분명 v19 == 9 와 !memcmp(v6, &buf2, 9uLL) 이부분을 모두 만족했으니 성공을 한거겠죠? 그러면 if 들어가기전에 bp를 걸고 v19의 값을 한번 찍어봅시당..
참고로 v19 값은 제가 일부러 이름을 flag로 바꿈 ㅎㅎ;; 해당 변수의 위치는 아래와 같습니다.
rsp+0x30
if ( flag == 9 ~~~~) 부분에 bp를 걸고 rsp+0x30 값을 찍어보니 아래와 같습니다. 읭...? 분명 메인코드에는 0으로 초기화하는 부분만 있고 따로 변수를 어케하지않는데.. 왜 9 값이 들어가있지..? 그리고 여러번 더 테스트하다보니 저기 들어가는 값이 내가 입력하는 문자열의 길이만큼 들어가는 사실을 알게됩니다.
if와 변수 초기화 사이에 내가 입력한 문자열의 수 만큼 해당 flag 값을 세팅해주는게 저 인풋할때 call 하는 함수밖에 없습니다. 그래서 flag 초기화 해줄 때 한번 bp를 걸고 사용자 입력 함수에서 값을 1 더해주는 곳 같이 보이는곳에 bp를 걸고 rsp+0x30(변수값이 위치하는 주소)를 디버깅해보니 실제로 rsp+0x30 여기 값이 계속 1씩 더해지는것을 확인할 수 있었습니다.
아... 사용자 입력을 받는 함수의 특정 변수가 flag와 같은 주소값을 가지고 있구나 -_-; 그래서 딱히 프로그램 패치 없이도 패스워드 입력 시 정답이 출력되는것을 확인할 수 있었습니다.
문제는 아래처럼 Buf1[2] 다음에 flag 값이 스택에 위치하게 되고 void *Buf1[2] 라서 Buf1[0], Buf1[1]의 주소만 가지고 있는데 Buf1[2]를 참조해버리니 flag가 있는곳으로 침범해버려서 별도로 flag 값을 초기화 또는 할당하지 않아도 해당 값이 바뀌는 결과를 가져왔다.
문제 자체는 쉽게풀었는데 원리까지 싹 이해하는데는 좀 시간걸렸다... 이거 난이도 1.5맞음? 아놔... 갈길이멀다
'Technical Docs > Reversing' 카테고리의 다른 글
crackemes.one - 5 (0) | 2024.08.05 |
---|---|
Crackmes.one - 4 (7) | 2024.07.24 |
Crackmes.one - 3 (4) | 2024.07.22 |
Crackmes.one - 1 (0) | 2024.07.17 |