๋ฌธ์ : DreamHack โ ์๋น์ค ์ด์ฉ ์ฝ๊ด ๋ถ๋ฅ: misc / crypto ๋์ด๋: ๐ฅ Bronze 3 FLAG:
DH{Do_Y0u_Know_EUC-KR_enc0ding?}
๋ฌธ์ ์ค๋ช ์ ์ด๋ ๋ค.
๋๋ฆผ์ด๋ ๋๋ฆผํต์ ์ ์ฉํ ์ ์๋น์ค ์ด์ฉ ์ฝ๊ด์ ์์ฑํ์ด์. ๊ทธ๋ฐ๋ฐ ์ด์ฉ ์ฝ๊ด์ด ์ ์ฅ๋ ํ์ผ์ด ์๋ชป๋ ๋ฐฉ์์ผ๋ก ์ธ์ฝ๋ฉ๋์ด ๋๋ฆผ์ด๊ฐ ๋นํฉ์ค๋ฌ์ํ๊ณ ์์ด์. ์ฌ๋ฌ๋ถ์ด ๋๋ฆผ์ด๋ฅผ ๋์ ์ด์ฉ ์ฝ๊ด์ ๋ณต์ํด ์ฃผ์ธ์.
๋ฐ์ ๊ฑด tos.txt ํ๋. ์๋ฒ๋ ์๊ณ ๋ฐ์ด๋๋ฆฌ๋ ์๋ค.
"์๋ชป๋ ๋ฐฉ์์ผ๋ก ์ธ์ฝ๋ฉ" ์ด๋ผ๋ ํ ๋ฌธ์ฅ์ด ๋ฌธ์ ์ ์ ๋ถ์ธ๋ฐ, ์ฌ๊ธฐ์ ๋งํ๋ ์ธ์ฝ๋ฉ์ด ํ ๊ฒน์ด ์๋๋ค. ๊ฒ์ hex ๋ฅผ ๋ฒ๊ธฐ๋ฉด ๋ ๋ฒ์งธ ์ธ์ฝ๋ฉ์ด ๋์ค๊ณ , ๊ทธ๊ฑธ ๋ฒ๊ธฐ๋ฉด ์ธ ๋ฒ์งธ๊ฐ ๋์จ๋ค. ๋ง์ง๋ง ๊ฒน์ ๋์ผ๋ก๋ ์ ๋ ์ ๋ณด์ธ๋ค.
file extracted/tos.txt && wc -c extracted/tos.txt && head -c 96 extracted/tos.txt && echo
ํ์ผ์ 51,071๋ฐ์ดํธ์ธ๋ฐ file ์ด "very long lines (51050)" ์ด๋ผ๊ณ ์๋ ค ์ค๋ค.
์ฆ ์ฌ์ค์ ํ ์ค์ง๋ฆฌ ํ์ผ์ด๋ค. ์๋จธ๋ฆฌ์ Encoded toc (hex) ๋ผ๋ ํค๋๊ฐ ๋ถ์ด ์๋ค.
tos ๋ฅผ toc ๋ก ์ ์ด ๋ ๊ฑด ์ถ์ ์์ ์คํ๋ก ๋ณด์ธ๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | ์๋น์ค ์ด์ฉ ์ฝ๊ด |
| ๋์ด๋ | ๐ฅ Bronze 3 |
| ๋ถ๋ฅ | misc / crypto |
| ์ ๊ณต ํ์ผ | tos.txt (ASCII, 51,071 B) |
| ์๋ฒ | ์์ โ ์์ ์คํ๋ผ์ธ |
| ํต์ฌ ๊ธฐ๋ฒ | hex โ EUC-KR โ ์ ๊ฐ(fullwidth) ASCII โ NFKC ์ ๊ทํ |
| ํจ์ | ๋ณธ๋ฌธ์ DH{...} ๋ชจ์์ ๋ฏธ๋ผ๊ฐ ๋ฐํ ์๋ค |
ํ์ด ํ๋ฆ์ ์งง๋ค. hex ๋ฅผ ๋๋๋ฆฌ๊ณ , UTF-8 ์ด ์๋๋ผ EUC-KR ๋ก ์ฝ๊ณ , ๋ณต์๋ ์ฝ๊ด์์ ํ๊ธ ์ฌ์ด์ ์์ธ ์ ๊ฐ ๋ผํด ๋ฌธ์๋ง ๊ณจ๋ผ๋ด NFKC ๋ก ์ ๊ทํํ๋ฉด ๋๋๋ค.
์งง์ง๋ง ๊ฐ ๋จ๊ณ๋ง๋ค ํ ๋ฒ์ฉ ๊ฑธ๋ ค ๋์ด์ง ์๋ฆฌ๊ฐ ์๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ EUC-KR ์ด๋ผ๋ "์๋ชป๋ ์ธ์ฝ๋ฉ"
์ง๊ธ ํ๊ตญ์ด ํ ์คํธ๋ ๊ฑฐ์ ์ ๋ถ UTF-8 ์ด๋ค. ํ์ง๋ง UTF-8 ์ด์ ์๋ EUC-KR(๊ณผ ๊ทธ ํ์ฅ์ธ CP949)์ด ์ฌ์ค์ ํ์ค์ด์๊ณ , ๊ด๊ณต์ ๋ฌธ์๋ ์ค๋๋ ์์คํ ์ด ๋ฑ๋ ํ์ผ์๋ ์์ง๋ ๋จ์ ์๋ค.
๋์ ์ฐจ์ด๋ ๋จ์ํ๋ค.
| ํ๊ธ ํ ๊ธ์ | ASCII ํ ๊ธ์ | ์ฒซ ๋ฐ์ดํธ | |
|---|---|---|---|
| UTF-8 | 3๋ฐ์ดํธ | 1๋ฐ์ดํธ | 0xEA~0xED |
| EUC-KR | 2๋ฐ์ดํธ | 1๋ฐ์ดํธ | 0xB0~0xC8 ๋ฑ |
"์๋ชป ์ธ์ฝ๋ฉ๋๋ค" ๋ ๋ฌธ์ ์ค๋ช ์ ์ฌ์ค ์ฝ๋ ์ชฝ์ด ์ธ์ฝ๋ฉ์ ์๋ชป ๊ณ ๋ฅด๋ฉด ๊นจ์ง๋ค๋ ๋ป์ด๋ค. ํ์ผ ์์ฒด๋ ๋ฉ์ฉกํ๊ณ , EUC-KR ๋ก ์ฝ์ด์ผ ํ ๊ฑธ UTF-8 ๋ก ์ฝ์ผ๋ฉด ์ฒซ ๋ฐ์ดํธ๋ถํฐ ์คํจํ๋ค.
์ฌ๊ธฐ๊น์ง๋ ํํ ์ด์ผ๊ธฐ๋ค. ์ด ๋ฌธ์ ๊ฐ ํ ๊ฑธ์ ๋ ๋๊ฐ๋ ์ง์ ์ ๋ค์ ๋์จ๋ค.
๐ฌ ์ ์ฐฐ โ hex ํ ์ค์ ๋ฏ์ด๋ณธ๋ค
๋จผ์ ํ์ผ์ ์ค์ ๋ฐ์ดํธ๋ฅผ ๋ณธ๋ค.
xxd -l 224 extracted/tos.txt
0d 0a 0d 0a ๋ก ํค๋๊ฐ ๋๋๊ณ , ๊ทธ ๋ค๋ก๋ 3039, 6166 ๋ง ๋์จ๋ค.
ASCII ๋ก 09, af. ํค๋ ๋ง๋๋ก hex ๋ค.
51,050์๋๊น ๋๋๋ฆฌ๋ฉด 25,525๋ฐ์ดํธ๊ฐ ๋์จ๋ค. ์ง์๋ผ ์๋ ค ์์ง๋ ์๋ค.
๋๋๋ฆฐ ๋ฐ์ดํธ๋ ๋ฌด์จ ์ธ์ฝ๋ฉ์ธ๊ฐ
hex ๋ฅผ ํ๊ณ ๋์ ๋ฐ๋ก .decode() ๋ฅผ ๋ถ๋ฅด๋ฉด ์ ๋๋ค. ์ด๋ค ์ฝ๋ฑ์ด ๋ง๋์ง๋ถํฐ ํ์ธํ๋ค.
probe_encoding.py ๋ฅผ ๋ง๋ค์ด ํ๋ณด๋ฅผ ํ๋์ฉ ์ฐ๋ฌ ๋ดค๋ค.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""hex ๋ฅผ ๋๋๋ฆฐ ๋ค ์ด๋ค ๋ฌธ์ ์ธ์ฝ๋ฉ์ธ์ง ์ค์ ๋ก ์ฐ๋ฌ๋ณธ๋ค.
๋ถ์ ํจ๊ณผ๋ก extracted/tos.bin (hex ๋ฅผ ํผ ๋ ๋ฐ์ดํธ) ์ ๋จ๊ธด๋ค.
iconv ๊ฐ์ ์ธ๋ถ ๋๊ตฌ๋ก ๊ต์ฐจํ์ธํ ๋ ์ด ํ์ผ์ ์ด๋ค.
"""
import binascii
from pathlib import Path
import chardet
HERE = Path(__file__).resolve().parent
TOS = HERE / "extracted" / "tos.txt"
BIN = HERE / "extracted" / "tos.bin"
raw = TOS.read_bytes()
hex_line = raw.split(b"\r\n")[2]
blob = binascii.unhexlify(hex_line)
BIN.write_bytes(blob)
print(f"[hex] {len(hex_line)}์ -> {len(blob)}๋ฐ์ดํธ, {BIN.name} ๋ก ์ ์ฅ")
print(f"[hex] ์ 16๋ฐ์ดํธ : {blob[:16].hex(' ')}")
print()
for enc in ("utf-8", "utf-16-le", "euc-kr", "cp949", "johab"):
try:
s = blob.decode(enc)
except UnicodeDecodeError as e:
print(f"[{enc:<9}] FAIL {e.reason} @ offset {e.start}")
except LookupError:
print(f"[{enc:<9}] SKIP ์ฝ๋ฑ ์์")
else:
print(f"[{enc:<9}] OK {len(s)}๊ธ์ | ์ฒซ ์ค = {s.splitlines()[0]}")
print()
guess = chardet.detect(blob)
print(f"[chardet] encoding={guess['encoding']} confidence={guess['confidence']:.2f} language={guess['language']}")python3 probe_encoding.py
UTF-8 ์ ์ฒซ ๋ฐ์ดํธ(0xc0)์์ ๋ฐ๋ก ์ฃฝ๋๋ค. 0xc0 ์ UTF-8 ์์ ์์ ์ฐ์ด์ง ์๋ ๊ฐ์ด๋ค.
EUC-KR ๊ณผ CP949 ๋ ๋ ๋ค ํต๊ณผํ๊ณ ์ฒซ ์ค์ด ์ด์ฉ์ฝ๊ด ์ผ๋ก ์ฝํ๋ค.
chardet ๋ EUC-KR ์ ์ ๋ขฐ๋ 0.99 ๋ก ์ฐ๋๋ค.
CP949 ๋ EUC-KR ์ ์์ํธํ์ด๋ผ ๋ ๋ค ๋๋ ๊ฒ ์ ์์ด๋ค. ์ด ํ์ผ์๋ CP949 ํ์ฅ ์์ญ ๊ธ์๊ฐ ์๋ค๋ ๋ป์ด๊ธฐ๋ ํ๋ค.
ํ์ด์ฌ ๋ง๊ณ ์ธ๋ถ ๋๊ตฌ๋ก๋ ํ์ธ
ํ ๋๊ตฌ๋ง ๋ฏฟ๊ธฐ๋ณด๋ค ๊ณ์ด์ด ๋ค๋ฅธ ๊ฒ์ผ๋ก ํ ๋ฒ ๋ ๋ณธ๋ค. iconv ๋ glibc ๊ฐ ์ ๊ณตํ๋ ๋ณํ๊ธฐ๋ค.
iconv -f UTF-8 -t UTF-8 extracted/tos.bin | wc -c; iconv -f EUC-KR -t UTF-8 extracted/tos.bin | sed -n "1,4p"
iconv -f UTF-8 -t UTF-8 ์ "์ด ๋ฐ์ดํธ์ด์ด ์ ํจํ UTF-8 ์ธ๊ฐ" ๋ฅผ ๋ฌป๋ ๊ด์ฉ์ ์ธ ๋ฐฉ๋ฒ์ด๋ค.
0๋ฐ์ดํธ๋ฅผ ๋ด๋๊ณ ์ค๋ฅ๋ก ๋๋๋ค. EUC-KR ๋ก ์ง์ ํ๋ฉด ์ฝ๊ด์ด ๊ทธ๋๋ก ๋์จ๋ค.
์ฃผ์ํ์ฌ ํฐ์ค๋ฆฌํ๊ตญ(์ดํ 'ํ์ฌ')๊ฐ ์ ๊ณตํ๋ Dreamhack ์๋น์ค... โ ์ง์ง ์ฝ๊ด์ฒ๋ผ ์๊ธด ๋ฌธ์๋ค.
๋ฌธ์ ์ค๋ช
์ด "์ด ์คํ ๋ฆฌ๋ ํฝ์
" ์ด๋ผ๊ณ ๋ชป๋ฐ์ ๋์ผ๋ ๋ด์ฉ์ ๊ทธ๋๋ก ๋ฏฟ์ ๊ฒ ์๋๋ค.
๐ ์ฝ์ง โ ๋ ๋ฒ ํ์ง์๋ค
์ฒซ ๊ฒน์ ๋ฒ๊ธฐ๊ณ ๋๋ฉด 15,016๊ธ์์ง๋ฆฌ ์ฝ๊ด์ด ์์ ๋ค์ด์จ๋ค. ์ฌ๊ธฐ์ DH ๋ฅผ ์ฐพ์ผ๋ฉด ๋ญ๊ฐ ๋์จ๋ค.
๊ทธ๋ฐ๋ฐ ๊ทธ๊ฒ ํจ์ ์ด์๋ค.
โถ๐ ์ฝ์ง 1 โ ๋๋๊ณ ๋ฐํ ์๋ ๋ฏธ๋ผ ํ๋๊ทธ
๋ณต์๋ ๋ณธ๋ฌธ์ DH{ ๋ก ์ฐพ์ผ๋ฉด ๋ฑ ํ ๊ตฐ๋ฐ ๊ฑธ๋ฆฐ๋ค. ์ 27์กฐ๋ค.
iconv -f EUC-KR -t UTF-8 extracted/tos.bin | grep -a -n -A1 "์ 27์กฐ"
์กฐ๋ฌธ ์ ๋ชฉ์ด ์ 27์กฐ (ํ๋๊ทธ) ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ณธ๋ฌธ์ ์ด๋ ๊ฒ ์ ํ ์๋ค.
๋ฌธ์์ด
DH{New_Dreamhack_Terms_Of_Service}๋ ํ๋๊ทธ๊ฐ ์๋๋๋ค.
์ฝ๊ณ ๋์์ผ ์์๋ค. ํ์๋ ๋ง๊ณ ๋ด์ฉ๋ ๊ทธ๋ด๋ฏํด์ ๊ทธ๋ฅ ๋์ ธ ๋ณผ ๋ปํ๋ค. ์ถ์ ์๊ฐ ์น์ ํ๊ฒ ์๋๋ผ๊ณ ์ ์ด ๋ ๋์ ์ ์ถ ์๋๋ ์ ํ์ง๋ง, "์ฐพ์๋ค" ๋ก ๋๋ด๋ฉด ์ ๋๋ค๋ ์ ํธ๋ฅผ ์ฌ๊ธฐ์ ๋ฐ์ ์ ์ด๋ค.
grep ์ -a ๋ฅผ ๋ถ์ธ ๊ฒ๋ ์ด์ ๊ฐ ์๋ค. ์ด ์คํธ๋ฆผ์๋ CR ์ด ์์ฌ ์์ด์ GNU grep ์ด
๋ฐ์ด๋๋ฆฌ๋ก ํ์ ํด ๋ฒ๋ฆฌ๋ ์ผ์ด ์์๋ค. -a ์์ด ๋๋ฆฌ๋ฉด ๋งค์น๊ฐ ์๋๋ฐ๋ ์กฐ์ฉํ ์ข
๋ฃ์ฝ๋ 1 ๋ก ๋๋๋ค.
โถ๐ ์ฝ์ง 2 โ 0xA3 ์ ๊ทธ๋ฅ grep ํ๋ค๊ฐ ์คํ 79๊ฐ
๋ค์ ๋์ค์ง๋ง ์ง์ง ํ๋๊ทธ๋ EUC-KR ์ A3 ํ์ ๋ค์ด ์๋ค.
๊ทธ๊ฑธ ์๊ณ ๋์ "๊ทธ๋ผ 0xA3 ๋ฐ์ดํธ๋ง ์ธ๋ฉด ๋๊ฒ ๋ค" ํ๊ณ ๋ฐ์ดํธ ๋จ์๋ก ํ์๋๋ ์๋ฑํ ๊ฐ์ด ๋์๋ค.
runs = [m for m in re.finditer(rb'(?:\xa3.)+', b)] # 43๊ฐ โ ๋๋ถ๋ถ ๊ฐ์งEUC-KR ์ ๊ฐ๋ณ ๊ธธ์ด๋ค. 0xA3 ์ ์ ๊ฐ ๊ธ์์ ์ ๋ ๋ฐ์ดํธ์ด๊ธฐ๋ ํ์ง๋ง,
์ ํ ๋ค๋ฅธ ํ๊ธ์ ๋ท๋ฐ์ดํธ๋ก๋ ๋์จ๋ค. ํ์ผ ์ ์ฒด์์ 0xA3 ๋ฐ์ดํธ๋ 79๊ฐ์ธ๋ฐ
๊ทธ ์ค ์ค์ ์ ๋๋ 36๊ฐ๋ฟ์ด๋ค.
์ ๊ท์์ผ๋ก ๋ฐ์ดํธ๋ฅผ ํ์ผ๋ฉด ์ด ๋์ ๊ตฌ๋ถํ์ง ๋ชปํ๋ค. ์ฒซ ๋ฐ์ดํธ๋ถํฐ ์์๋๋ก ๊ฑธ์ผ๋ฉด์
"์ง๊ธ ์ด ๋ฐ์ดํธ๊ฐ ๊ธ์์ ์์์ธ๊ฐ" ๋ฅผ ์ ์งํด์ผ ํ๋ค. solve.py ์ walk_euckr() ๊ฐ ๊ทธ ์ผ์ ํ๋ค.
์ด๋ฐ ์ข
๋ฅ์ ์ฐฉ๊ฐ์ UTF-8 ์์๋ ์ ์ ์๊ธด๋ค. UTF-8 ์ ํ์ ๋ฐ์ดํธ๊ฐ ํญ์ 10xxxxxx ๋ผ
๋ฐ์ดํธ๋ง ๋ด๋ ์ ๋์ธ์ง ์ ์ ์๊ฒ ์ค๊ณ๋ผ ์๊ธฐ ๋๋ฌธ์ด๋ค. EUC-KR ์๋ ๊ทธ๋ฐ ์ฅ์น๊ฐ ์๋ค.
๐ฃ ํต์ฌ โ ํ๊ธ ์ฌ์ด์ ์์ธ ์ ๊ฐ ASCII
๋ฏธ๋ผ์ ์์ง ์์ผ๋ ค๋ฉด "์ฝ๊ด ๋ณธ๋ฌธ์ ์์ด์๋ ์ ๋ ๊ฒ" ์ ์ฐพ์์ผ ํ๋ค. ์ ์์ ์ธ ํ๊ตญ์ด ์ฝ๊ด์ด๋ผ๋ฉด ๊ธ์๋ ํ๊ธ ์๋๋ฉด ASCII๋ค. ๊ทธ ๋ฐ์ ๊ฒ์ด ๋จ์ผ๋ฉด ๊ทธ๊ฒ ์ฌ์ด์ง ๊ฒ์ด๋ค.
๊ทธ๋์ ๊ธ์๋ฅผ ์ข
๋ฅ๋ณ๋ก ์ธ๋ charscan.py ๋ฅผ ๋ง๋ค์๋ค.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""๋ณต์๋ ์ฝ๊ด ๋ณธ๋ฌธ์ ๊ธ์๋ฅผ ์ข
๋ฅ๋ณ๋ก ์ธ์ด 'ํ๋ ๊ธ์' ๋ฅผ ์ฐพ์๋ธ๋ค.
์ฝ๊ด์ ํ๊ธ + ASCII ๋ก๋ง ์ด๋ค์ ธ์ผ ์ ์์ด๋ค. ๊ทธ ๋ฐ์ ๊ธ์๊ฐ ๋จ์ผ๋ฉด
๊ทธ๊ฒ ๊ณง ์ถ์ ์๊ฐ ์ฌ์ด ๋ ๊ฒ์ด๋ค.
"""
import unicodedata
from collections import Counter
from pathlib import Path
HERE = Path(__file__).resolve().parent
BIN = HERE / "extracted" / "tos.bin"
text = BIN.read_bytes().decode("euc-kr")
def kind(ch: str) -> str:
o = ord(ch)
if o < 0x80:
return "ASCII"
if 0xAC00 <= o <= 0xD7A3:
return "ํ๊ธ"
if 0xFF01 <= o <= 0xFF5E:
return "์ ๊ฐASCII"
return "๊ธฐํ"
tally = Counter(kind(c) for c in text)
print(f"๋ณธ๋ฌธ {len(text)}๊ธ์")
for k, n in tally.most_common():
print(f" {k:<9} {n:>6}")
print()
print("ํ๊ธ/ASCII ๊ฐ ์๋ ๊ธ์ (๋ง์ ์):")
odd = Counter(c for c in text if kind(c) not in ("ASCII", "ํ๊ธ"))
for ch, n in odd.most_common(12):
print(f" U+{ord(ch):04X} {ch} x{n:<3} {unicodedata.name(ch, '?')}")
print(f" ... ์ด {len(odd)}์ข
")
print()
hits = [i for i, c in enumerate(text) if kind(c) == "์ ๊ฐASCII"]
print(f"์ ๊ฐ ASCII {len(hits)}๊ธ์, ์์น {hits[0]}~{hits[-1]}")
print(f"๊ทธ ๊ตฌ๊ฐ ์๋ฌธ : {text[hits[0]:hits[-1] + 1]}")python3 charscan.py
15,016๊ธ์ ์ค ์ ๊ฐ ASCII ๊ฐ ์ ํํ 36๊ธ์๋ค. ์์น๋ 11743~11791 ํ ๊ตฐ๋ฐ์ ๋ชฐ๋ ค ์๋ค. ๊ทธ ๊ตฌ๊ฐ์ ๊ทธ๋๋ก ์ฐ์ด ๋ณด๋ฉด ์ด๋ ๊ฒ ๋์จ๋ค.
โค ํ๋๊ทธ๋ ๋ค์ ๋ฌธ์์ด์ ๏ผฎ๏ผฆ๏ผซ๏ผฃ ์ ๊ทํํ ๊ฒ์ ๋๋ค : ๏ผค๏ผจ๏ฝ๏ผค๏ฝ๏ผฟ๏ผน๏ผ๏ฝ๏ผฟโฆ๏ฝ
์ 18์กฐ (์ ๋ฃ ์ฝํ
์ธ ) ์ ๋ค์ฏ ๋ฒ์งธ ํญ์ด๋ค. ์ ๋ฃ ์ฝํ
์ธ ์กฐ๋ฌธ ํ๋ณตํ์ ํ๋๊ทธ ์๋ด๊ฐ ๋ผ์ด ์๋ค.
โ ~โช ๊ฐ์ ์๋ฌธ์์ โโ ๊ฐ์ ์ธ์ฉ๋ถํธ๋ "๊ธฐํ" ๋ก ์กํ๋๋ฐ, ๊ทธ๊ฑด ์ฝ๊ด ๋ฌธ์์ ์๋ ํํ ๊ฒ๋ค์ด๋ผ
๊ฐ์ด ๋์ฌ ๋ฟ ๋จ์๋ ์๋๋ค.
๏ผฎ๏ผฆ๏ผซ๏ผฃ ๋ผ๋ ์ง์์ด ์์ฒด๋ ์ ๊ฐ์ผ๋ก ์ฐ์ฌ ์๋ค๋ ๊ฒ ์ฌ๋ฐ๋ค. ์ ๊ฐ ๊ตฌ๊ฐ 36๊ธ์ = ์ง์์ด 4๊ธ์ + ํ๋๊ทธ 32๊ธ์๋ค.

์ ๋์ ์ ๋์๋
์ ๊ฐ(fullwidth) ๋ฌธ์๋ ์ ๋์ฝ๋ U+FF01~U+FF5E ์ ์๋, ASCII ๋ฅผ ํ๊ธ ํญ์ ๋ง์ถฐ ๋ค์ ๊ทธ๋ฆฐ ๊ธ์๋ค.
๏ผค ์ D ๋ ์๋ฏธ์ ๊ฐ์ ๊ธ์์ธ๋ฐ ์ฝ๋ํฌ์ธํธ๊ฐ ๋ค๋ฅด๋ค.
EUC-KR ์์๋ ์ด๊ฒ ๋ ์๊ถ๋ค. ์ ๊ฐ ASCII ๋ A3 ํ์ ํต์งธ๋ก ๋ค์ด ์๊ณ ,
๋ท๋ฐ์ดํธ๊ฐ ์ ํํ ASCII | 0x80 ์ด๋ค.
| ๊ธ์ | ์ ๋์ฝ๋ | EUC-KR ๋ฐ์ดํธ |
|---|---|---|
D | U+0044 | 44 (1๋ฐ์ดํธ) |
๏ผค | U+FF24 | a3 c4 (2๋ฐ์ดํธ) |
{ | U+007B | 7b (1๋ฐ์ดํธ) |
๏ฝ | U+FF5B | a3 fb (2๋ฐ์ดํธ) |
์ฆ ์ ๊ฐ์ผ๋ก ์ฐ์ธ ํ๋๊ทธ๋ EUC-KR ์์์ ํ๊ธ๊ณผ ๋๊ฐ์ 2๋ฐ์ดํธ ๊ธ์๋ก ์กด์ฌํ๋ค.
grep DH ๋ก๋ ์ ์กํ๊ณ , strings ๋ก๋ ์ ์กํ๋ค. ์ค์ ๋ฐ์ดํธ๋ฅผ ๋ณด๋ฉด ๋ถ๋ช
ํ๋ค.
xxd -s 19968 -l 144 extracted/tos.bin
0x4e1a ๋ถํฐ a3c4 a3c8 a3fb a3c4 a3ef ... ๊ฐ ์ด์ด์ง๋ค.
a3c4 โ 0xc4 - 0x80 = 0x44 โ D. a3c8 โ H. a3fb โ {.
๋ท๋ฐ์ดํธ์์ 0x80 ๋ง ๋นผ๋ฉด ๊ทธ๋๋ก ASCII ๊ฐ ๋๋ค.
์ด ๊ท์น ๋๋ถ์ ์ ๋์ฝ๋ ํ
์ด๋ธ ์์ด๋ ๋ต์ ๋ผ ์ ์๋ค โ
EUC-KR ์คํธ๋ฆผ์ ๊ฑธ์ผ๋ฉด์ ์ ๋ ๋ฐ์ดํธ๊ฐ 0xA3 ์ธ ๊ธ์๋ง ๋ชจ์ผ๋ฉด ๋๋ค.
solve.py ๋ ์ด ๊ฒฝ๋ก๋ฅผ ๊ต์ฐจ๊ฒ์ฆ์ฉ์ผ๋ก ๋ฐ๋ก ๋๋ฆฐ๋ค.
๋ง์ง๋ง ์ด์ โ NFKC
๋ณธ๋ฌธ์ด ์ํค๋ NFKC ๋ ์ ๋์ฝ๋ ์ ๊ทํ ํ์ ์ค ํ๋๋ค.
NFC ๊ฐ "๋ชจ์์ด ๊ฐ์ ๊ฒ์ ํฉ์น๋" ์์ค์ด๋ผ๋ฉด, NFKC ๋ ํธํ ๋ฌธ์๋ฅผ ๋ํ ๋ฌธ์๋ก ์ ๋๋ค.
์ ๊ฐ ๏ผค โ D, โ โ 1, ๏ฌ ํฉ์ โ fi ๊ฐ์ ๋ณํ์ด ์ฌ๊ธฐ์ ์ผ์ด๋๋ค.
ํ์ด์ฌ์์๋ unicodedata.normalize("NFKC", s) ํ ์ค์ด๋ค.
์ ๊ฐ 32๊ธ์๋ฅผ ์ฌ๊ธฐ์ ํต๊ณผ์ํค๋ฉด ํ๋ฒํ ASCII ํ๋๊ทธ๊ฐ ๋๋ค.
๐ฏ ๊ต์ฐจํ์ธ โ CyberChef ๋ก ๊ฐ์ ๋ต์ด ๋์ค๋๊ฐ
๋ด ์คํฌ๋ฆฝํธ๋ง์ผ๋ก ๊ฒฐ๋ก ์ ๋ด๋ฉด ์คํฌ๋ฆฝํธ ๋ฒ๊ทธ์ ์ ๋ต์ ๊ตฌ๋ถํ ์ ์๋ค. ๊ทธ๋์ ์์ ํ ๋ค๋ฅธ ๊ตฌํ์ผ๋ก ํ ๋ฒ ๋ ํ์ธํ๋ค. CyberChef ๋ ๋ธ๋ผ์ฐ์ ์์ ๋๋ ์ธ์ฝ๋ฉ ๋ณํ ๋๊ตฌ๋ค.
ํ๋๊ทธ๊ฐ ๋ ๊ตฌ๊ฐ์ hex 238์๋ฅผ ์๋ผ From Hex โ Decode text (EUC Korean 51949) ๋ ์ํผ์ ๋ฃ์๋ค.
๋ ์ํผ์ ์
๋ ฅ์ URL ํด์์ ์ค๋ ค ์์ด ์ฃผ์๋ง ์ด๋ฉด ๊ทธ๋๋ก ์ฌํ๋๋ค.

Output ์ ๋ณด๋ฉด ๊ธ์ ์ฌ์ด๊ฐ ์ ๋ํ ๋ฒ์ด์ ธ ์๋ค. ๊ทธ๊ฒ ์ ๊ฐ์ด๋ผ๋ ์ฆ๊ฑฐ๋ค.
๋ธ๋ผ์ฐ์ ํฐํธ๋ก ๋ ๋ํด๋ ๏ผค๏ผจ๏ฝ ๋ DH{ ๋ณด๋ค ๋์ ๋๊ฒ ๋๋ค.
ํ์ด์ฌ์ด ์ค ๋ต๊ณผ ๊ฐ์ ๋ฌธ์์ด์ด ๋์์ผ๋ ์ฝ๋ฑ ์ ํ์ ํ๋ฆฌ์ง ์์๋ค.
๐ Full Exploit
์ง๊ธ๊น์ง์ ํ๋จ์ ํ ํ์ผ๋ก ๋ฌถ์๋ค. tos.txt ๋ง ์์ผ๋ฉด ํ๋๊ทธ๊น์ง ๊ฐ๋ค.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
DreamHack "์๋น์ค ์ด์ฉ ์ฝ๊ด" (Bronze 3, misc/crypto) โ solve
tos.txt ๋ ์ด๋ ๊ฒ ์๊ฒผ๋ค.
Encoded toc (hex)\r\n
\r\n
c0ccbfebbee0b0fc0d0ac1a631c0e5... <- 51050์ ํ ์ค
ํ์ด๋ ์ธ ๊ฒน์ด๋ค.
1) ํค๋๋ฅผ ๋ผ๊ณ hex ๋ฅผ ๋ฐ์ดํธ๋ก ๋๋๋ฆฐ๋ค.
2) ๊ทธ ๋ฐ์ดํธ๋ UTF-8 ์ด ์๋๋ผ EUC-KR ์ด๋ค (= ๋ฌธ์ ๊ฐ ๋งํ๋ "์๋ชป๋ ์ธ์ฝ๋ฉ").
3) ๋ณต์๋ ์ฝ๊ด ๋ณธ๋ฌธ ์์์ flag ๋ '์ ๊ฐ(fullwidth) ๋ผํด' ์ผ๋ก ์ฌ์ด์ ธ ์๋ค.
๋ณธ๋ฌธ ์ 18์กฐ โค ๊ฐ "๋ค์ ๋ฌธ์์ด์ NFKC ์ ๊ทํํ ๊ฒ" ์ด๋ผ๊ณ ์๋ ค ์ค๋ค.
EUC-KR ์์ ์ ๊ฐ ASCII ๋ A3 ํ์ ์๊ณ , ๋ท๋ฐ์ดํธ๋ ์ ํํ (ASCII | 0x80) ์ด๋ค.
'D' = 0x44 -> '๏ผค' = 0xA3 0xC4
๊ทธ๋์ EUC-KR ์คํธ๋ฆผ์ ๊ฑธ์ผ๋ฉฐ ์ ๋ ๋ฐ์ดํธ๊ฐ 0xA3 ์ธ ๊ธ์๋ง ๋ชจ์ผ๋ฉด
์ ๋์ฝ๋ ํ
์ด๋ธ ์์ด๋ ๊ฐ์ ๋ต์ด ๋์จ๋ค (verify_a3_row ๊ฐ ๊ทธ๊ฑธ ํ์ธํ๋ค).
"""
import binascii
import sys
import unicodedata
from pathlib import Path
HERE = Path(__file__).resolve().parent
TOS = HERE / "extracted" / "tos.txt"
def load_hex(path: Path) -> bytes:
"""ํค๋ 2์ค์ ๋ผ๊ณ hex ๋ณธ๋ฌธ๋ง ๋ฐ์ดํธ๋ก ๋๋๋ฆฐ๋ค."""
raw = path.read_bytes()
parts = raw.split(b"\r\n")
hex_line = parts[2]
print(f"[1] {path.name}: {len(raw)}๋ฐ์ดํธ, ํค๋={parts[0].decode()!r}")
print(f"[1] hex ๋ณธ๋ฌธ {len(hex_line)}์ -> {len(hex_line)//2}๋ฐ์ดํธ")
return binascii.unhexlify(hex_line)
def pick_encoding(blob: bytes) -> str:
"""UTF-8 ์ ๊นจ์ง๊ณ EUC-KR ๋ก๋ง ์ฝํ๋ค๋ ๊ฒ์ ์ค์ ๋ก ๋ณด์ฌ ์ค๋ค."""
for enc in ("utf-8", "euc-kr"):
try:
blob.decode(enc)
except UnicodeDecodeError as e:
print(f"[2] {enc:<7} ๋์ฝ๋ ์คํจ - {e.reason} (offset {e.start})")
else:
print(f"[2] {enc:<7} ๋์ฝ๋ ์ฑ๊ณต")
good = enc
return good
def walk_euckr(blob: bytes):
"""EUC-KR ์คํธ๋ฆผ์ ๊ฑธ์ผ๋ฉฐ (๋ฐ์ดํธ์ด, ๊ธ์) ๋ฅผ ์์๋๋ก ๋ด๋๋๋ค.
๋ฐ์ดํธ๋ฅผ ๊ทธ๋ฅ grep ํ๋ฉด ์ ๋๋ค. 0xA3 ์ ๋ค๋ฅธ ํ๊ธ์ '๋ท๋ฐ์ดํธ' ๋ก๋
๋์ค๊ธฐ ๋๋ฌธ์(ํ์ผ ์ ์ฒด์ 79๊ฐ, ๊ทธ ์ค ์ ๋๋ 36๊ฐ๋ฟ) ์ ๋/ํํ ๊ตฌ๋ถ์ ์ง์ผ์ผ ํ๋ค.
"""
i = 0
while i < len(blob):
n = 2 if blob[i] >= 0x80 else 1
chunk = blob[i:i + n]
yield chunk, chunk.decode("euc-kr")
i += n
def verify_a3_row(blob: bytes) -> str:
"""์ ๋ ๋ฐ์ดํธ๊ฐ 0xA3(์ ๊ฐ ASCII ํ)์ธ ๊ธ์๋ง ๋ชจ์๋ค."""
out = []
for chunk, ch in walk_euckr(blob):
if len(chunk) == 2 and chunk[0] == 0xA3:
out.append(chr(chunk[1] - 0x80))
naive = blob.count(b"\xa3")
print(f"[5] 0xA3 ์ ๋จ์ ๋ฐ์ดํธ๋ก ์ธ๋ฉด {naive}๊ฐ (ํํ๋ฐ์ดํธ ์คํ ํฌํจ)")
print(f"[5] EUC-KR ์คํธ๋ฆผ์ ๊ฑธ์ด์ ๊ณ ๋ฅธ ์ ๊ฐ ๊ธ์: {len(out)}๊ฐ")
return "".join(out)
def extract_flag(text: str) -> str:
"""์ ๊ฐ ๋ผํด์ด ์ฐ์์ผ๋ก ์ด์ด์ง๋ ๊ฐ์ฅ ๊ธด ๊ตฌ๊ฐ์ ๋ฝ์ NFKC ์ ๊ทํํ๋ค."""
runs, cur = [], ""
for ch in text:
if 0xFF01 <= ord(ch) <= 0xFF5E:
cur += ch
else:
if cur:
runs.append(cur)
cur = ""
if cur:
runs.append(cur)
runs.sort(key=len, reverse=True)
print(f"[4] ์ ๊ฐ ๊ตฌ๊ฐ {len(runs)}๊ฐ, ๊ฐ์ฅ ๊ธด ๊ฒ {len(runs[0])}์")
print(f"[4] ์๋ฌธ ๊ทธ๋๋ก : {runs[0]}")
return unicodedata.normalize("NFKC", runs[0])
def find_decoy(text: str) -> str:
"""์ 27์กฐ๊ฐ '์ด๊ฑด ํ๋๊ทธ๊ฐ ์๋๋ค' ๋ผ๊ณ ๋ชป๋ฐ์ ๋ ๋ฏธ๋ผ."""
decoy = "DH{New_Dreamhack_Terms_Of_Service}"
line = next(l for l in text.splitlines() if decoy in l)
print(f"[3] ๋ณธ๋ฌธ์ ๋ฐํ ๋ฏธ๋ผ {decoy}")
print(f"[3] ์ 27์กฐ ์๋ฌธ : {line.strip()}")
return decoy
def main() -> int:
blob = load_hex(TOS)
enc = pick_encoding(blob)
text = blob.decode(enc)
find_decoy(text)
flag = extract_flag(text)
a3 = unicodedata.normalize("NFKC", verify_a3_row(blob))
assert flag in a3, "A3 ํ ๊ต์ฐจ๊ฒ์ฆ ์คํจ"
print(f"[6] ๊ต์ฐจ๊ฒ์ฆ(0xA3 ํ ์ ์ฒด) : {a3}")
print()
print(f"FLAG: {flag}")
return 0
if __name__ == "__main__":
sys.exit(main())python3 solve.py
[5] ์ค์ด ์ฝ์ง 2์์ ์ด์ผ๊ธฐํ ๊ทธ ์ฐจ์ด๋ค. 0xA3 ๋ฐ์ดํธ๋ 79๊ฐ์ธ๋ฐ ์ค์ ์ ๊ฐ ๊ธ์๋ 36๊ฐ.
๋ ๊ฒฝ๋ก(์ ๋์ฝ๋ ๋ฒ์๋ก ๊ณ ๋ฅด๊ธฐ / A3 ํ ๋ฐ์ดํธ๋ก ๊ณ ๋ฅด๊ธฐ)๊ฐ ๊ฐ์ ๋ฌธ์์ด์ ๋ด๋์์ assert ๊ฐ ํต๊ณผํ๋ค.
[6] ์ NFKC ๋ค ๊ธ์๊ฐ ์์ ๋ถ์ด ๋์ค๋ ๊ฑด ์ง์์ด๊น์ง ์ ๊ฐ์ด๋ผ ๊ฐ์ด ๋ธ๋ ค ์จ ๊ฒ์ด๋ค.
ํ๋๊ทธ๋ ์ด๊ฒ์ด๋ค.
DH{Do_Y0u_Know_EUC-KR_enc0ding?}โป๏ธ ์ฌํ
๋ฌธ์ ํด๋๋ง ์์ผ๋ฉด ํ ๋ฒ์ ๋ค์ ๋์๊ฐ๋๋ก reproduce.sh ๋ฅผ ๋จ๊ฒจ ๋๋ค.
์ ๋ต๊ณผ ๋์กฐ๊น์ง ์คํฌ๋ฆฝํธ๊ฐ ์ค์ค๋ก ํ๋ค.
#!/usr/bin/env bash
# ์๋น์ค ์ด์ฉ ์ฝ๊ด (DreamHack Bronze 3, misc/crypto) โ ํ ๋ฐฉ ์ฌํ
# tos.txt ๋ง ์์ผ๋ฉด flag ๊น์ง ๊ฐ๋ค. ์๋ฒยท๋คํธ์ํฌ ๋ถํ์.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ .json'))['flag'])" 2>/dev/null || echo '')
if [ ! -f extracted/tos.txt ]; then
echo "โ extracted/tos.txt ๊ฐ ์์ต๋๋ค (challenge_2189.zip ์ ํ์ด ์ฃผ์ธ์)"
exit 1
fi
# tos.bin(hex ๋ฅผ ํผ ๋ ๋ฐ์ดํธ)์ probe_encoding.py ๊ฐ ๋ง๋ ๋ค. solve.py ๋ tos.txt ๋ง์ผ๋ก๋ ๋๋ค.
_probe=$(python3 probe_encoding.py 2>&1) || true
OUT=$(timeout 120 python3 solve.py 2>&1) || true
echo "$OUT" | tail -8
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | tail -1)
if [ -n "$FLAG" ] && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
echo; echo "โ
PASS $FLAG"; exit 0
fi
echo; echo "โ FAIL (์ป์ ๊ฐ: '${FLAG:-์์}' / ๊ธฐ๋: '$EXPECT')"; exit 1./reproduce.sh
๊ธฐ๋๊ฐ EXPECT ๋ ๊ฐ์ ํด๋์ ๋ฌธ์ .json ์์ ์ฝ๋๋ฐ, ์ด ํ์ผ์ ๋ฐฑ์
๋จ๊ณ์์ ์๊ธด๋ค.
์์ง ์๋ ์์ ์ด๋ผ ์ ์คํ์ "ํ๋๊ทธ๋ฅผ ๋ฝ์๋๋๊ฐ" ๋ง ๋ณด๊ณ ํต๊ณผํ๋ค.
๋ฌธ์ .json ์ด ๋์ด๋ฉด ๊ทธ๋๋ถํฐ๋ ๊ฐ๊น์ง ๋์กฐํ๋ค.
grep ์์ tail -1 ์ ์ด ์ด์ ๋ ์๋ค. ์ถ๋ ฅ์ DH{ ๋ก ์์ํ๋ ๋ฌธ์์ด์ด ๋ ๋ฒ ๋์จ๋ค โ
๋ฏธ๋ผ์ ์ง์ง. head -1 ๋ก ์ก์ผ๋ฉด ๋ฏธ๋ผ๋ฅผ ์ ๋ต์ผ๋ก ์ฐฉ๊ฐํ ์ฑ ์กฐ์ฉํ ์คํจํ๋ค.
๋ก์ปฌ ํ๊ฒฝ์ Ubuntu 25.10 / Python 3.13 / chardet 5.2.0 ์ด๋ค.
chardet ์ ์ ์ฐฐ์ฉ์ด๋ผ ์์ด๋ solve.py ๋ ๋๋ค.
๐ ๊ฒฐ๋ก
๋ฏธ๋ผ๋ "ํ์์ด ๋ง๋ ๊ฒ" ์ผ๋ก ์จ๋ค.
์ด ๋ฌธ์ ์์ ๊ฐ์ฅ ์ ๊ฑธ๋ฆฌ๋ ์๋ฆฌ๋ ์ธ์ฝ๋ฉ์ด ์๋๋ผ ์ 27์กฐ๋ค.
DH{...} ํํ๋ฅผ ์ฐพ์๋ง์ ์์ด ์ ์ถ ๋ฒํผ์ผ๋ก ๊ฐ๋ค. ์ถ์ ์๊ฐ ์๋๋ผ๊ณ ์ ์ด ๋์ง ์์์ผ๋ฉด
์ค๋ต ํ ๋ฒ์ ๋จน์์ ๊ฒ์ด๋ค.
CTF ๋ ์ค๋ฌด๋ , ํจํด ๋งค์นญ์ผ๋ก ์ฐพ์ ๊ฒ์ "์ฐพ์๋ค" ๊ฐ ์๋๋ผ "ํ๋ณด๋ฅผ ํ๋ ์ป์๋ค" ๋ค.
๋ณด์ด์ง ์๋ ๊ธ์๋ ๋ณด์ด๋ ๊ธ์์ ๊ฐ์ ์ทจ๊ธ์ ๋ฐ์ ์ ์๋ค.
์ ๊ฐ ๏ผค ์ ๋ฐ๊ฐ D ๋ ์ฌ๋ ๋์๋, ๋๋ถ๋ถ์ ํํฐ์๋ ๋ค๋ฅธ ๊ธ์๋ค.
๊ทธ๋ฐ๋ฐ NFKC ์ ๊ทํ๋ฅผ ๊ฑฐ์น๋ฉด ๊ฐ์์ง๋ค. ์ด ์ฑ์ง์ ์ค์ ์ทจ์ฝ์ ์ ์ฌ๋ฃ๊ฐ ๋๋ค.
๊ฒ์ฆ์ ๋จผ์ ํ๊ณ ์ ๊ทํ๋ฅผ ๋์ค์ ํ๋ฉด ๊ทธ๋๋ก ์ฐํ๊ฐ ๋๋ค. ๏ผscript๏ผ ๋ ํ๊ทธ ๋ธ๋๋ฆฌ์คํธ๋ฅผ
ํต๊ณผํ์ง๋ง NFKC ๋ฅผ ์ง๋๋ฉด <script> ๊ฐ ๋๋ค. ํ์ด์ฌ ์๋ณ์, ํ์ผ ๊ฒฝ๋ก, ์ฌ์ฉ์๋ช
์ค๋ณต ๊ฒ์ฌ์์
๊ฐ์ ํจํด์ด ๋ฐ๋ณตํด์ ๋ฐ๊ฒฌ๋ผ ์๋ค.
์์๋ ์ ๊ทํ โ ๊ฒ์ฆ์ด๋ค. ๋ฐ๋๋ก ํ๋ฉด ๊ฒ์ฌํ ๋ฌธ์์ด๊ณผ ์ค์ ๋ก ์ฐ์ด๋ ๋ฌธ์์ด์ด ๋ฌ๋ผ์ง๋ค.
์ธ์ฝ๋ฉ์ ์ถ์ธกํ์ง ๋ง๊ณ ์ธก์ ํ๋ค.
chardet, iconv -f X -t X, CyberChef โ ๊ณ์ด์ด ๋ค๋ฅธ ๋๊ตฌ ์ธ ๊ฐ๊ฐ ๊ฐ์ ๋ต์ ์ฃผ๋ฉด ๊ทธ๋ ๋ฏฟ๋๋ค.
์ด ๋ฌธ์ ๋ EUC-KR ํ๋๋ง ๋งํ๋ฉด ๋๋ ์ฌ์ด ์ถ์ด์ง๋ง, ์ค๋ฌด์์ ๋ง๋๋ ํ์ผ์
CP949 ์ EUC-KR ์ด ์์ฌ ์๊ฑฐ๋ UTF-8 ๋ก ๋ ๋ฒ ์ธ์ฝ๋ฉ๋ ๊ฒ๋ค์ด๋ผ ํ ๋ฒ์ ์ ๋ง๋๋ค.
๊ฐ๋ณ ๊ธธ์ด ์ธ์ฝ๋ฉ์์ ๋ฐ์ดํธ ๋จ์ ๊ฒ์์ ํ๋ฆฐ๋ค.
0xA3 ์ ๊ทธ๋ฅ ์ธ๋ฉด 79๊ฐ, ์ค์ ๋ก๋ 36๊ฐ์๋ค. UTF-8 ์ ํ์ ๋ฐ์ดํธ๊ฐ 10xxxxxx ๋ก ํ์๋ผ ์์ด
๋ฐ์ดํธ๋ง ๋ด๋ ๊ฒฝ๊ณ๋ฅผ ์ ์ ์์ง๋ง EUC-KR ์๋ ๊ทธ๋ฐ ํ์์ด ์๋ค.
๋ ๊ฑฐ์ ์ธ์ฝ๋ฉ์ ๋ค๋ฃฐ ๋๋ ์คํธ๋ฆผ์ ์ฒ์๋ถํฐ ๊ฑธ์ด์ผ ํ๋ค. ์ด๊ฑด ํ์ ๊ตฌํ์์
๊ฒฝ๊ณ ํผ๋ ์ทจ์ฝ์ ์ด ์๊ธฐ๋ ์๋ฆฌ์ด๊ธฐ๋ ํ๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ