해보자고

[드림핵] rev-basic-0 본문

리버싱

[드림핵] rev-basic-0

초코맛동산 2024. 4. 11. 12:06

# 문제

문제 링크 | https://dreamhack.io/wargame/challenges/14

rev-basic-0

Reversing Basic Challenge #0 이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니다. 해당 바이너리를 분석하여 correct를 출

dreamhack.io

이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니다.
해당 바이너리를 분석하여 correct를 출력하는 입력값을 찾으세요!

 


 
 

1. main 함수 찾기 

 

 

f8 단축키로 쭉 main 함수를 찾기 위해 코드를 진행하였다. 그 중 해당 call 명령어를 지나면 input: 창이 활성화되는 것을 확인하였고 해당 부분이 main 함수라고 유추하였다. 
 
 

2. 실행 흐름 파악하기

 
main 함수에 들어와서 실행 흐름을 살펴보면 간단하게 2 가지로 정리할 수 있다.
 
1. Input : 문자열로 사용자에게 문자열을 입력 받는다.
2. test eax, eax 의 결과값에 따라 je를 통해 Wrong 으로 분기하거나, Correct가 나오게 된다. 
 
문제는 Correct 값이 나오게 하는 flag를 찾는 것이므로 2번 과정에 중점을 두고 어셈블리 코드를 살펴보기로 하였다. test eax, eax는 무슨 역할을 하는가?

1. JE 명령어
- 보통 CMP와 함께 쓰인다.
- JE 는 Jump Equal 즉, 같으면 JE 의 주소로 이동하라는 명령어 이다. 
- JE가 '같다' 라고 이해할 수 있는 신호는 ZF가 1일 때 이다.  

2. CMP의 원리
형식: CMP dest, src
원리: dest 에서 src를 묵시적으로 빼서 값이 같은지 비교한다. 
결과: 값이 0이면 (두 값이 같다) ZF = 1로 set된다.  

3. TEST의 원리 
형식: TEST opr1, opr2
원리: 두 오퍼랜드를 AND 연산한다. 
(그러나 CMP와 같이 그 값을 저장하지는 않는다.) 
결과: 값이 0이면 ZF = 1로 set된다. 

3.1 TEST EAX, EAX
- 목적: EAX의 값이 0인지 아닌지를 판단 (AND 연산은 두 값이 0일때만 0)
- 0이면 ZF는 1로 set -> ZE 이동
- 0이 아니면 ZF = 0 -> ZE 무시 

 
 
우리가 원하는 건 eax가 0이 아니여서 je 명령문을 무시하는 값을 찾는 것이다. 
 
 

3. 디버깅

 
한 줄 한 줄 분석해보도록 하자. 
 

 
rcx에는 내가 Input : 에 입력한 AAAAA가 들어가 있는 것을 확인할 수 있었다. 
rcx값을 rsp + 8 위치에 저장하고 있다.
 

이후 rsp에서 38을 빼준다.
 

rdx에 7FF76BCB2220 주소를 저장해준다. 해당 주소에는 Compar3_the_str1ng 문자열이 저장되어 있다.
 

이후 JMP.&strcmp 를 호출한다.
그 이후 test eax, eax로 eax가 0 인지를 확인하고, 0이 아니면 mov dword ptr ss:[rsp+20], 0 으로 분기되고, 그 이후로 rsp + 20 주소에 0이 저장되고 다시 rsp+20(값이 0인) 주소의 값을  eax에 이동 시킨다.
 
반대로 eax가 0이면 1을 rsp+20에 저장하고, rsp+20(값이 1인) 주소의 값을  eax에 이동 시킨다.
 
=> 이번 함수의 값에 어떤 값이 리턴되느냐에 따라 다시 test eax, eax를 통해 correct가 될지, wrong이 될지 결정된다. 그러므로 다시 JMP.&strcmp 함수를 확인해보자.  
 
 
JMP.&strcmp 함수를 확인해봤을 때 어떻게 동작하는지는 사실 이해하기는 어려웠고, AAAAA를 입력했을 때 왜 eax에 0이 할당되어 Wrong을 출력하는지만을 확인해보았다.
 

 
sbb 어셈블리어는 레지스터끼리 빼는 연산을 하는데 CF를 고려하여 빼는 어셈블리어이다. CF는 이전 연산 결과에서 빌림이 발생하면 set된다. 현재 CF를 확인하면 1이 set되어 있는 것을 볼 수 있다 따라서 같은 값을 빼주었지만 - 1 연산을 추가로 더하여 다음 명령어를 실행했을 때 -1 이라는 결과가 나온다. 
 
따라서 rax에는 0xfffffffffffffffffffffff의 결과 값이 저장되고, 다음 명령어인 1과 or 연산을 진행한다.
-1은 어차피 모든 비트가 1인 상태이므로 or 연산을 해도 1이기에 0xffff...ffff가 유지된다. 

 
그리고 위에서 언급한 test eax, eax가 나온다. 

 

 
우리는 eax에 1이 리턴되어야 하는데 이미 eax가 -1로 0 이 아니기에 0이 리턴된다. 그럼 AAAAA를 입력했을 때 Wrong이 나오게 될 것이다. 
 
그럼 중간에 strcmp 에서 발견한 문자열이 flag가 아닐까 추측하고 해당 값을 넣어주면 
 

 
correct가 나오는 것을 확인할 수 있었다.
 
 
사실 JMP.&strcmp 에서 두 값을 비교한다는 것을 어셈블리어를 통해서는 이해하지 못했지만 추측했을 때 flag는 맞아서 찝찝하긴 하다. 
 
 
 
 
 
 
 

 

'리버싱' 카테고리의 다른 글

[리버싱] rev-basic-4  (0) 2024.04.28
[드림핵]rev-basic-3  (0) 2024.04.27
[드림핵]rev-basic-2  (0) 2024.04.27
[크랙미 ] crackme1  (1) 2024.04.21
[드림핵]rev-basic-1  (0) 2024.04.12