| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
- adsp독학
- wargame
- OpenClaw
- randzzz
- OS
- 26년 1회
- cloudgoat
- 병행성
- rev-basic-3
- AWS
- OWASP TOP 10
- 세그먼테이션
- user pool
- ADsP시험
- dreamhack
- Juice Shop
- rev-basic-4
- ADsP합격
- 라운드로빈
- ADsP
- Pacu
- HRRN
- id pool
- ADsP교재
- writeup
- Simple Crack Me
- 운영체제
- 교체 정책
- ADsP책추천
- 시나공ADsP
- Today
- Total
chon
[Dreamhack] rev-basic-4 Write up 본문
[ 문제 링크 | https://dreamhack.io/wargame/challenges/18 ]
rev-basic-4
Reversing Basic Challenge #4이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니다. 해당 바이너리를 분석하여 correct를 출
dreamhack.io
[ 문제 설명 ]

[ 문제 풀이 ]


먼저 chall4.exe 파일을 직접 실행해보면, 콘솔 창에서 Input : 이라는 문구와 함께 입력값을 요구하는 걸 볼 수 있다.
즉, 사용자가 입력한 값이 내부에서 검증되어 맞으면 “Correct”, 틀리면 “Wrong” 이 출력되는 구조임을 알 수 있다.
이제 실제 검증 로직이 어떻게 동작하는지 확인하기 위해 IDA를 이용해 바이너리를 분석해보자.

IDA로 디컴파일한 결과, main 함수 내부는 입력값을 받고 이를 특정 함수로 넘겨 검증하는 구조임을 확인할 수 있다.
상단에서는 스택 초기화 및 보안 쿠키 설정( __security_cookie )이 수행되고, 하단의 박스로 표시한 부분에서 핵심 로직이 실행된다.
흐름을 따라가 보면 다음과 같다.
- "Input : " 문자열이 출력되며, 사용자 입력을 받을 준비를 한다.
- 입력값은 버퍼에 저장된 뒤, sub_140001000 함수의 인자로 전달된다.
- 해당 함수는 입력값이 올바른지 검증하는 역할을 수행한다.
- 함수의 반환값은 eax 레지스터에 저장되며, 바로 아래의 test eax, eax 명령어를 통해 반환값이 0인지 여부가 확인된다.
- 반환값이 0이면 jz 분기를 따라 "Wrong"이 출력되고,
0이 아니면 "Correct"가 출력된다.
main 함수 분석

IDA의 함수 목록에서 main을 더블클릭해 들어가면 다음과 같은 C 형태의 코드로 디컴파일된 내용을 볼 수 있다.
해당 함수에서는 v4라는 256바이트 크기의 버퍼를 초기화하고(memset), "Input : " 문자열을 출력한 뒤 사용자 입력을 v4에 저장한다. 그 후 sub_140001000(v4) 함수를 호출하여 입력값을 검증한다.
이 함수의 반환값이 0이 아닐 경우 "Correct", 그 외에는 "Wrong"을 출력하는 구조이다.
즉, 프로그램의 핵심은 sub_140001000 함수 내부의 연산 로직에 있으며, 이를 분석함으로써 어떤 입력값이 올바른 flag인지 유추할 수 있다. 따라서 다음 단계에서는 해당 함수로 진입하여, 입력값이 어떻게 처리되고 어떤 연산을 거치는지 분석한다.

main에서 넘겨받은 입력값(v4)은 sub_140001000 함수(의 인자 a1)로 들어온다. 여기가 진짜 'Correct'를 결정하는 핵심 로직이다.
코드를 보면 for 반복문이 나오는데, 이걸 분석해보면 i가 0부터 0X1C전까지, 즉 0부터 27번까지 28번을 반복한다.
입력값의 길이가 28이라는 걸 알 수 있다.
비교 로직을 보면 if 문 안에서 한 연산의 결과랑 byte_140003000[i]을 비교한다.
만약 두 값이 다르다면 함수가 종료되고, 28의 비교를 모두 통과하면 return 1;을 만나서 "Correct"가 출력된다.

byte_140003000에 있는 28개의 바이트 값을 역으로 니블 스왑하면 원래의 입력값인 flag가 나온다.
dup는 'duplicate'의 약자니까 2 dup(0C6h)는 0xC6, 0xC6을 의미하고, 마지막 28번째 값은 5dup()의 첫 번째 0x00이다. C언어 문자열의 끝을 알려주는 NULL값까지 검사하는 것이다.
역연산 시 필요한 28개의 바이트를 순서대로 나열하면 다음과 같다.
[ 0x24, 0x27, 0x13, 0xC6, 0xC6, 0x13, 0x16, 0xE6, 0x47, 0xF5,
0x26, 0x96, 0x47, 0xF5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xC6,
0x56, 0xF5, 0xC3, 0xC3, 0xF5, 0xE3, 0xE3, 0x00 ]
역연산 코드
# byte_140003000에 저장된 데이터
answer_key = [
0x24, 0x27, 0x13, 0xC6, 0xC6, 0x13, 0x16, 0xE6, 0x47, 0xF5,
0x26, 0x96, 0x47, 0xF5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xC6,
0x56, 0xF5, 0xC3, 0xC3, 0xF5, 0xE3, 0xE3, 0x00
]
flag = ""
# 각 바이트를 순회하며 니블 스왑(Nibble Swap) 수행
for b in answer_key:
swapped_byte = ((b << 4) & 0xFF) | (b >> 4)
flag += chr(swapped_byte)
print(f"[*] Flag: {flag}")

위에서 찾은 Flag를 입력하면 된다.
Br1ll1ant_bit_dr1bble_<<_>>
[ 문제 해결 ]


'Reversing > Dreamhack Write up' 카테고리의 다른 글
| [Dreamhack] Recover (0) | 2026.01.16 |
|---|---|
| [Dreamhack] Happy New Year! (0) | 2026.01.14 |
| [Dreamhack] Simple Crack Me Write up (0) | 2025.12.14 |
| [Dreamhack] randzzz Write up (0) | 2025.11.20 |
| [Dreamhack] rev-basic-3 Write up (0) | 2025.11.02 |