[๐ฑ ์์น] ๋น๊ต ์ง์ XORยท์ฐ์ ์ด ๋ผ์ด๋ ๊ฒ์ฆ ๋ฃจํ โ DreamHack rev-basic-3 ํ์ด โ ZINO2026-06-08ยท1๋ถ ์ฝ๊ธฐยท
rev-basic-2๊น์ง๋ ์ ๋ต์ด ํ๋ฌธ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ์ ๋ฐํ ์์๋ค. 3๋ฒ์ ๋น๊ต ์ง์ ์ ๊ธ์๋ฅผ ํ ๋ฒ ์ฃผ๋ฌผ๋ฌ ๋๋ค โ input[i]๋ฅผ i๋ก XORํ๊ณ 2*i๋ฅผ ๋ํ ๊ฐ์ด .data์ ํ
์ด๋ธ๊ณผ ๊ฐ์์ผ ํต๊ณผํ๋ค. XOR๋ ๋ง์
๋ ์ญ์ฐ์ฐ์ด ์์ผ๋, ํ
์ด๋ธ์ ๋ ๋ด ๊ฑฐ๊พธ๋ก ํ๋ฉด ๊ทธ๋๋ก ์ ๋ต์ด ๋์จ๋ค.
๋ฌธ์ : DreamHack โ rev-basic-3 (Reversing Basic Challenge #3)
๋ถ๋ฅ: Reversing
๋์ด๋: ๐ฑ ์์น
FLAG: DH{I_am_X0_xo_Xor_eXcit1ng}
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|
| ๋ฌธ์ ๋ช
| rev-basic-3 |
| ๋์ด๋ | ๐ฑ ์์น |
| ๋ถ๋ฅ | Reversing |
| ์ ๊ณต ํ์ผ | chall3.exe (PE32+ x86-64, Windows ์ฝ์) |
| ํต์ฌ ๊ธฐ๋ฒ | ๋น๊ต ์ง์ ์ input[i] ^ i + 2*i ๋ณํ์ ๋ผ์ ๋ ๊ฒ์ฆ โ ์ญ์ฐ์ฐ์ผ๋ก ๋ณต์ |
rev-basic-2์ ๋ง์ง๋ง์ "๋ค์ ๋ฒํธ๋ถํฐ๋ ๋น๊ต ์ง์ ์ XORยท์ฐ์ ๊ฐ์ ๋ณํ์ด ๋ผ์ด๋ค์ด ์ญ์ฐ์ด ํ์ํด์ง๋ค"๊ณ ์ ์ด ๋๋๋ฐ, 3๋ฒ์ด ์ ํํ ๊ทธ ์๋ฆฌ๋ค.
0~2๋ฒ์ ์ ๋ต์ด ์ด๋์ ๋ด๊ฒผ๋ (.rdata ๋ฌธ์์ด / ์ฝ๋ ์ immediate / .data ์ ์ ๋ฐฐ์ด) ๊ฒฐ๊ตญ ํ๋ฌธ ๊ทธ๋๋ก ๋น๊ต๋๋ค. 3๋ฒ์ ๋ด ์
๋ ฅ์ cmp ์ง์ ์ ํ ๋ฒ ์ฃผ๋ฌด๋ฅธ๋ค. input[i]๋ฅผ ์ธ๋ฑ์ค i๋ก XORํ๊ณ ๋ฅผ ๋ํ ๊ฐ์ด ํ
์ด๋ธ๊ณผ ๊ฐ์์ผ ํต๊ณผํ๋ค. ๋ณํ์ด ๋ผ์์ง๋ง ๋ ๋ค ์ญ์ฐ์ฐ์ด ์๋ ์ฐ์ฐ์ด๋ผ, ํ
์ด๋ธ๋ง ๋ ๋ด๋ฉด ๊ฑฐ๊พธ๋ก ํ๋ฆฐ๋ค.
์ด ๊ธ์ด ๋์์ด ๋๋์?
2*i
file chall3.exe && strings -a chall3.exe | grep -iE "Input|Correct|Wrong|256s"
๊ธฐ๋ณธ ์ ๋ณด โ PE32+ x86-64 ์ฝ์ ๋ฐ์ด๋๋ฆฌ / strings์ InputยทCorrectยทWrong
๐ฌ ๋ถ์
fileยทstrings๋ ์๋ฆฌ์ฆ ๋ด๋ด ๋๊ฐ๋ค. Input : ๋ก ์
๋ ฅ์ ๋ฐ๊ณ (%256s), 0x140001000(check)์ผ๋ก ๊ฒ์ฆํ ๋ค Correct / Wrong์ผ๋ก ๊ฐ๋ฆฐ๋ค.
main์ ๋ฐ๋ผ๊ฐ ๋ณด๋ฉด ํ๋ฆ์ด ๋จ์ํ๋ค.
; main ๋ฐ์ท
lea rax, [rsp+0x20] ; ์
๋ ฅ ๋ฒํผ (256๋ฐ์ดํธ, 0์ผ๋ก memset)
...
call 0x1400011b0 ; printf("Input : ")
lea rdx, [rsp+0x20]
call 0x140001210 ; scanf("%256s", buf)
lea rcx, [rsp+0x20]
call 0x140001000 ; โ
check(buf)
test eax, eax
je .wrong ; 0 ์ด๋ฉด Wrong
... "Correct" ...
.wrong:
... "Wrong" ...
ํต์ฌ์ check๋ค. ์ธ๋ฑ์ค i๋ฅผ 0๋ถํฐ ์ฌ๋ฆฌ๋ ๋ฃจํ ํ๋๋ก ๋์ด ์๋ค.
; check(input) @ 0x140001000
mov dword [rsp], 0 ; i = 0
.loop:
movsxd rax, [rsp] ; rax = i
cmp rax, 0x18 ; i < 24 ? (0x18 = 24)
jae .ok ; 24๊ธ์ ๋ค ํต๊ณผ โ return 1
lea rcx, [0x140003000] ; ์ ๋ต ํ
์ด๋ธ ๋ฒ ์ด์ค
movzx eax, byte [rcx+rax] ; eax = table[i] (๊ธฐ๋๊ฐ)
mov rdx, [rsp+0x20] ; rdx = ์
๋ ฅ ๋ฒํผ ์ฃผ์
movzx ecx, byte [rdx+rax] ; ecx = input[i]
xor ecx, [rsp] ; ecx = input[i] ^ i โ โ
XOR
mov edx, [rsp] ; edx = i
lea ecx, [rcx+rdx*2] ; ecx = (input[i]^i) + 2*i โ โ
์ฐ์
cmp eax, ecx ; table[i] == (input[i]^i)+2*i ?
je .next ; ๋ง์ผ๋ฉด ๋ค์ ๊ธ์
xor eax, eax ; ํ๋๋ผ๋ ํ๋ฆฌ๋ฉด return 0 โ Wrong
jmp .ret
.next:
inc dword [rsp] ; i++
jmp .loop
.ok:
mov eax, 1 ; ์ ๋ถ ํต๊ณผ โ return 1 โ Correct
.ret:
ret
2๋ฒ๊ณผ ๊ณจ๊ฒฉ์ ๊ฐ๋ค. ๋ค๋ฅธ ๊ฑด ๋น๊ต์์ ๋ผ์ด๋ ๋ ์ค์ด๋ค. xor ecx, [rsp]๋ก input[i]๋ฅผ ์ธ๋ฑ์ค i์ XORํ๊ณ , lea ecx, [rcx+rdx*2]๋ก 2*i๋ฅผ ๋ํ๋ค(lea์ rdx*2๊ฐ ๊ณฑ์
ยท๋ง์
์ ํ ์ค์ ์ฒ๋ฆฌํ๋ค). ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํ
์ด๋ธ ๊ฐ๊ณผ ๋ง์ถฐ ๋ณธ๋ค. ๋ฃจํ ์ํ์ 0x18(24)์ด๋ผ 24์นธ์ ๊ฒ์ฌํ๋ค.
objdump -d -M intel --start-address=0x140001000 --stop-address=0x14000105d chall3.exe | grep -E "^140001"
objdump โ check ํจ์. xor ecx,[rsp] / lea ecx,[rcx+rdx*2] / cmp eax,ecx
Ghidra ๋์ปดํ์ผ๋ฌ(analyzeHeadless)๋ ๊ทธ ๋ณํ์ ํ ์ค๋ก ๋ณด์ฌ์ค๋ค โ DAT_140003000[i] != (input[i] ^ i) + i*2์ด๋ฉด ๋ฃจํ๋ฅผ ๋๋๋ค(๋ถ์ผ์น).
Ghidra headless ๋์ปดํ์ผ โ check() ๋ฃจํ๊ฐ (input[i]^i)+i*2๋ฅผ DAT_140003000[i] ํ
์ด๋ธ๊ณผ ๋น๊ต
์ ๋ฆฌํ๋ฉด ๋ชจ๋ i์ ๋ํด ์ด ์์ด ์ฑ๋ฆฝํด์ผ ํ๋ค.
table[i] == (input[i] ^ i) + 2*i
๐ฃ ํต์ฌ โ ์ญ์ฐ์ฐ ๋ ๋ฒ์ด๋ฉด ๋
๋ณํ์ด ๋ผ์๋ค๊ณ ๊ฒ๋จน์ ๊ฑด ์๋ค. XOR๋ ๋ง์
๋ ์๊ธฐ๋ฅผ ๋๋๋ฆฌ๋ ์ฐ์ฐ์ด ์๋ค.
+ 2*i โ - 2*i ๋ก ๋๋๋ฆฐ๋ค.
^ i โ ๊ฐ์ i๋ก ํ ๋ฒ ๋ XORํ๋ฉด ์๋ ๊ฐ์ด๋ค (a ^ i ^ i == a).
๊ฒ์ฆ์์ input[i]์ ๋ํด ํ๋ฉด ์ด๋ ๊ฒ ๋๋ค.
table[i] == (input[i] ^ i) + 2*i
table[i] - 2*i == input[i] ^ i
(table[i]-2i)^i == input[i]
๊ฒ์ฆ โ ์ญ์ฐ. XORยท๋ง์
์ ๊ฑฐ๊พธ๋ก ํ๋ฉด input[i]๊ฐ ๋์จ๋ค
ํ ๊ฐ์ง๋ง ํ์ธํ๋ฉด ๋๋ค. ๋น๊ต๋ 32๋นํธ cmp์ธ๋ฐ table[i]๋ movzx๋ก 0255 ๋ฒ์๋ค. ์ฆ (input[i]^i) + 2*i ๊ฐ๋ 0255 ์์ ๋ค์ด์์ผ ๊ฐ์์ง ์ ์๋ค(2*i๋ก ๋์ณ 256 ์ด์์ด ๋๋ฉด ์ ๋ ์ ๋ง๋๋ค). ์ถ์ ์๊ฐ ๋ต์ ASCII๋ก ๋ง์ถฐ ๋์ผ๋ ์ด ์กฐ๊ฑด์ ์์ฐํ ๋ง์กฑํ๋ค โ ๊ทธ๋ฅ ์๋๋ก ๋นผ๊ณ XORํ๋ฉด ๋๋ค.
๐ฏ ํ์ด โ ํ
์ด๋ธ 24๋ฐ์ดํธ๋ฅผ ๋ ์ ์ญ์ฐ
0x140003000์ .data์์ ๋ ๋ณธ๋ค. 24๋ฐ์ดํธ๊ฐ ๋์ด์ ์๋ค.
objdump -s -j .data chall3.exe | sed -n "1,7p"
objdump -s -j .data โ ์ ๋ต ํ
์ด๋ธ 24๋ฐ์ดํธ @ 0x140003000
140003000 49 60 67 74 63 67 42 66 80 78 69 69 7b 99 6d 88
140003010 68 94 9f 8d 4d a5 9d 45
์ด 24๋ฐ์ดํธ๊ฐ table[i]๋ค. ๊ฐ ์นธ์ input[i] = (table[i] - 2*i) ^ i๋ฅผ ์ ์ฉํ๋ฉด ์ ๋ต ๋ฌธ์์ด์ด ๋์จ๋ค.
#!/usr/bin/env python3
# rev-basic-3 solve
# check(): for i in 0..23: table[i] == (input[i] ^ i) + 2*i
# => input[i] = (table[i] - 2*i) ^ i
table = bytes.fromhex(
"49 60 67 74 63 67 42 66 80 78 69 69 7b 99 6d 88 68 94 9f 8d 4d a5 9d 45".replace(" ", "")
)
raw = bytes(((table[i] - 2 * i) & 0xFF) ^ i for i in range(len(table)))
flag = raw.rstrip(b"\x00").decode() # ๋ง์ง๋ง ๋ฐ์ดํธ๋ ์
๋ ฅ์ NUL ์ข
๋จ
print("table len :", len(table))
print("recovered :", raw) # ๋์ \x00 ๋ถ์
print("flag :", flag)
print("DH{%s}" % flag)
# check() ๋ก์ง๋๋ก ๋ฐ์ดํธ ๋จ์ ์๊ฐ๊ฒ์ฆ
ok = all(table[i] == (((raw[i]) ^ i) + 2 * i) for i in range(len(table)))
print("verify :", "PASS" if ok else "FAIL")
๋ง์ง๋ง 24๋ฒ์งธ ์นธ์ด ์ฌ๋ฏธ์๋ค. table[23] = 0x45(69), 2*23 = 46์ด๋ผ 69 - 46 = 23, ๊ฑฐ๊ธฐ์ ^ 23์ ํ๋ฉด 0์ด๋ค. ์ฆ 24๋ฒ์งธ๋ก ๊ฒ์ฌ๋๋ ๋ฐ์ดํธ๋ ๋(\0) ์ด๊ณ , ์
๋ ฅ ๋ฒํผ๊ฐ 0์ผ๋ก ์ฑ์์ ธ ์์ผ๋ ๊ทธ๋๋ก ํต๊ณผํ๋ค. ์ค์ ์ ๋ต ๊ธ์๋ ์ 23๊ฐ๋ค.
solve.py โ ํ
์ด๋ธ์ ์ญ์ฐํด flag ๋ณต์, ์๊ฐ๊ฒ์ฆ PASS
๋ณต์๋ ์
๋ ฅ๊ฐ์ I_am_X0_xo_Xor_eXcit1ng. ์ค์ ๋ฐ์ด๋๋ฆฌ์ ๋ฃ์ด ํ์ธํ๋ค. ๋ฆฌ๋
์ค๋ผ wine์ผ๋ก ๋๋ ธ๋ค.
# verify.py
import subprocess, os
env = dict(os.environ,
WINEPREFIX=os.path.expanduser("~/.wine_rev"),
WINEDEBUG="-all",
DISPLAY="") # wine์ ์์ ํค๋๋ฆฌ์ค๋ก
flag = "I_am_X0_xo_Xor_eXcit1ng" # DH{} ๋ ์ ์ถ์ฉ ๋ํผ์ผ ๋ฟ
r = subprocess.run(
["wine", "chall3.exe"],
input=flag + "\n",
capture_output=True, text=True, timeout=60, env=env,
)
print("Input :", flag)
print("Output:", r.stdout.strip())
verify.py โ wine์ผ๋ก ์ค์ ์คํ, Output์ Correct
Correct. ํ๋๊ทธ๋ DH{I_am_X0_xo_Xor_eXcit1ng}.
๐ ์ ๋ฆฌ
๋น๊ต ์ง์ ํ ์ค์ ๋์น๋ฉด ๋ต์ด ์ ๋์จ๋ค.
2๋ฒ๊น์ง๋ cmp ์๋ณ ์ค ํ์ชฝ์ด ๊ณง ์ ๋ต์ด์๋ค. 3๋ฒ์ ๊ทธ ์ฌ์ด์ xor ecx, [rsp]์ lea ecx, [rcx+rdx*2] ๋ ์ค์ด ๋ผ์ด ์๋ค. ์ด ๋ ์ค์ ๋นผ๊ณ ํ
์ด๋ธ๋ง ์ฝ์ผ๋ฉด 49 60 67 ... ๊ฐ์ ๊นจ์ง ๋ฐ์ดํธ๊ฐ ๋์จ๋ค. check๋ฅผ ๋๊น์ง ์ฝ์ด ๋ณํ์์ ์ ํํ ์ธ์ฐ๋ ๊ฒ ๋จผ์ ๋ค.
๊ฐ์ญ ์ฐ์ฐ์ ๊ฑฐ๊พธ๋ก ๋๋ฆฌ๋ฉด ๊ทธ๋ง์ด๋ค.
XOR๊ณผ ๋ง์
์ ๋ ๋ค ์ญ์ฐ์ฐ์ด ์๋ค. ๊ฒ์ฆ์ table[i] == (input[i]^i)+2*i๋ฅผ input[i]์ ๋ํด ํธ๋ ๊ฑด ์์ ์ดํญํ๋ ์์ค์ด๋ค. ์ด๋ฐ ์ฐ์ ยท๋นํธ ๋ณํ ๊ฒ์ฆ์ ๋๋ถ๋ถ ์ด๋ ๊ฒ ํ ์ค๋ก ์ญ์ฐ์ด ๋จ์ด์ง๋ค. ๋ณํ์ด ๋น๊ฐ์ญ(ํด์ ๋ฑ)์ด ๋๋ ์๊ฐ๋ถํฐ๊ฐ ์ง์ง ๋ฆฌ๋ฒ์ฑ์ ์์์ด๊ณ , rev-basic ๋จ๊ณ์์๋ ์์ง ๊ฑฐ๊ธฐ๊น์ง ๊ฐ์ง ์๋๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ