๋ฌธ์ : DreamHack โ babycmp ๋ถ๋ฅ: reversing ๋์ด๋: ๐ฅ Silver 2 FLAG:
DH{y0u_4r3_cmp__ma5t3r!}
๋ฌธ์ ์ค๋ช ์ ํ ์ค์ด ์ ๋ถ๋ค.
ํ๋๊ทธ๋ฅผ ์ฐพ์ ์ ์๊ฒ ๋์?
๋ฐ์ ํ์ผ๋ ํ๋๋ฟ์ด๋ค. babycmp.exe, 102,912๋ฐ์ดํธ. ์์ค๋ Dockerfile ๋ ์๊ณ ์๋ฒ๋ ์๋ค.
์ ๋ถ ์ด ์์ ๋ค์ด ์๋ค๋ ๋ป์ด๋ค.
file extracted/babycmp.exe && objdump -h extracted/babycmp.exe
PE32+, GUI ์๋ธ์์คํ
, 6์น์
. ๋์ ๋๋ ๊ฑด ํฌ๊ธฐ ๋ฐฐ๋ถ์ด๋ค. ์ฝ๋(.text)๋ 0x2d5c(11,612๋ฐ์ดํธ)
๋ฐ์ ์ ๋๋๋ฐ ๋ฆฌ์์ค(.rsrc)๊ฐ 0x115b0(70,576๋ฐ์ดํธ)์ผ๋ก ์ฌ์ฏ ๋ฐฐ๊ฐ ๋๋๋ค.
๋ก์ง์ด ์ ๊ณ ํ๋ฉด์ด ๋ง์ ํ๋ก๊ทธ๋จ, ์ฆ ๋ค์ด์ผ๋ก๊ทธ ํ๋์ง๋ฆฌ GUI ์ฑ์ด๋ค.
.data ๋ 0x400 ์ผ๋ก ์์์, ๋ญ๊ฐ ํ๋์ฝ๋ฉ๋ ๊ฐ์ด ์๋ค๋ฉด ๊ฑฐ๊ธฐ ๋ค ๋ค์ด๊ฐ๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | babycmp |
| ๋์ด๋ | ๐ฅ Silver 2 |
| ๋ถ๋ฅ | reversing |
| ์ ๊ณต ํ์ผ | babycmp.exe (PE32+ x86-64, GUI, 102,912๋ฐ์ดํธ) |
| ์คํ | MSVC 2022 ยท MFC(mfc140u.dll) ๋ค์ด์ผ๋ก๊ทธ |
| ํต์ฌ ์ทจ์ฝ์ / ๊ธฐ๋ฒ | ์ ๋ ฅ์ ์์์ด๊ณผ ํต์งธ๋ก ๋น๊ตํ๋ ์๋ฆฌ์ผ ๊ฒ์ฆ์ ์ญ์ฐ |
ํ๋ฆ์ ์ด๋ ๋ค. ๋ค์ด์ผ๋ก๊ทธ์ SERIAL ์ ๋ฃ๊ณ Check ๋ฅผ ๋๋ฅด๋ฉด, ๊ธธ์ดยท์ ๋์ฌยท๋ ๊ธ์๋ฅผ ๋จผ์ ๋ณด๊ณ
ํต๊ณผํ ์
๋ ฅ์ ํ ๊ธ์์ฉ int32 ๋ก ๋ถํ๋ฆฐ๋ค. ๊ทธ 24๊ฐ ๊ฐ์ .data ์ ๋ฐ์ ๋ 24๊ฐ์ ํต์งธ๋ก
๋น๊ตํ๋ค. ๋ณํ์ด ๋ ์ค๋ฟ์ด๋ผ ์๋ฆฌ๋ง๋ค ํ๋ณด๊ฐ ํ๋๋ก ๋จ์ด์ง๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ MFC ๋ค์ด์ผ๋ก๊ทธ ์ฑ์ด ๋จ๊ธฐ๋ ๊ฒ
MFC(Microsoft Foundation Classes)๋ Win32 API ๋ฅผ C++ ํด๋์ค๋ก ๊ฐ์ผ ์ค๋๋ GUI ํ๋ ์์ํฌ๋ค.
CDialog ๋ฅผ ์์ํ ํด๋์ค ํ๋์ ๋ฒํผ ํธ๋ค๋ฌ๋ฅผ ๋ถ์ด๋ฉด ์ฐฝ ํ๋์ง๋ฆฌ ํ๋ก๊ทธ๋จ์ด ๋ฐ๋ก ๋์จ๋ค.
๋ฆฌ๋ฒ์ฑ ์ชฝ์์ MFC ๋ฐ์ด๋๋ฆฌ๊ฐ ํธํ ์ด์ ๊ฐ ๋ ์๋ค.
ํ๋๋ ํ๋ฉด ๋ฌธ์์ด์ด ๋ฆฌ์์ค๋ก ๋ฐ๋ก ๋น ์ง๋ค๋ ๊ฒ. ๋ค์ด์ผ๋ก๊ทธ ํ
ํ๋ฆฟยท๋ผ๋ฒจยท๋ฉ์์ง๊ฐ
.rsrc ์ UTF-16 ์ผ๋ก ๋ชจ์ฌ ์์ด์, ์ฝ๋๋ฅผ ์ฝ๊ธฐ ์ ์ ํ๋ก๊ทธ๋จ์ด ๋ฌด์จ ๋ง์ ํ๋์ง๋ถํฐ ์ ์ ์๋ค.
๋ค๋ฅธ ํ๋๋ ํ๋ ์์ํฌ ์ฝ๋๊ฐ DLL ๋ก ๋น ์ง๋ค๋ ๊ฒ. mfc140u.dll ์ ์ํฌํธ๋ก ์ฐ๊ธฐ ๋๋ฌธ์
.text ์ ๋จ๋ ๊ฑด ๋๋ถ๋ถ ์ถ์ ์๊ฐ ์ง์ ์ด ์ฝ๋๋ค. ์ ์ ๋งํฌ๋ CRT ํจ์ ์์ฒ ๊ฐ ์ฌ์ด์์
main ์ ์ฐพ๋ ์๊ณ ๊ฐ ์๋ค.
๊ทธ๋์ ์์๋ ์์ฐ์ค๋ฝ๊ฒ "๋ฌธ์์ด โ ์ํฌํธ โ ๊ทธ ์ฌ์ด์ ๋ ์งง์ ์ฝ๋"๊ฐ ๋๋ค.
๐ฌ ์ ์ฐฐ โ ํ๋ก๊ทธ๋จ์ด ํ๋ ๋ง๋ถํฐ ์ฝ๋๋ค
๋ฆฌ์์ค ๋ฌธ์์ด์ UTF-16 ์ด๋ผ strings ์ -el ์ ์ค์ผ ๋์จ๋ค.
strings -n 4 -el extracted/babycmp.exe | grep -nE 'SERIAL|Check|GOT IT|Congra|failed|opps'
์ฌ์ฏ ์ค๋ก ํ๋ก๊ทธ๋จ์ ์ ๋ถ๊ฐ ๋๋ฌ๋๋ค. SERIAL: ๋ผ๋ฒจ๊ณผ Check ๋ฒํผ์ด ์๊ณ ,
๊ฒฐ๊ณผ๋ Congraturations! ์๋๋ฉด opps! try again! ์ด๋ค. ์คํ์๊น์ง ๊ทธ๋๋ก๋ค.
์ฑ๊ณต/์คํจ ๋ฌธ์์ด์ด ๋ฆฌ์์ค ์ธ๋ฑ์ค 8~11 ์ ๋๋ํ ๋ถ์ด ์๋ ๊ฒ๋ ํํธ๋ค. ๋ถ๊ธฐ๊ฐ ํ๋๋ฟ์ด๋ผ๋ ๋ป์ด๋ผ, ๊ฒ์ฌ ํจ์ ํ๋๋ง ์ฐพ์ผ๋ฉด ๋๋ค.
๋ค์์ ์ํฌํธ์ ๋น๋ ํ์ ์ด๋ค.
objdump -x extracted/babycmp.exe | grep 'DLL Name'; strings -n 8 extracted/babycmp.exe | grep -E 'neko_hat|[.]pdb'
mfc140u.dll ์ด ๋งจ ์์ด๋ค. ์์๋๋ก MFC ์ฑ์ด๊ณ , ๋๋จธ์ง๋ CRT ์ USER32ยทCOMCTL32 ๋ฟ์ด๋ค.
PDB ๊ฒฝ๋ก์ C:\Users\user\OneDrive\Desktop\babycmp\x64\Release\babycmp.pdb ๊ฐ ๋จ์ ์์ด
Release ๋น๋๋ผ๋ ๊ฒ๋ ํ์ธ๋๋ค.
๊ทธ๋ฆฌ๊ณ neko_hat. ๋ฌธ์ ์ถ์ ์ ๋๋ค์๊ณผ ๊ฐ์ ๋ฌธ์์ด์ด ๋ฐ์ด๋๋ฆฌ ์์ ๋ฐํ ์๋ค.
์ฌ๊ธฐ์ ํ ๊ฐ์ง ์ฃผ์ํ ๊ฒ ์๋ค. ์ถ๋ ฅ์ด neko_hat ์ด ์๋๋ผ neko_hatH ๋ค.
๋ค์ ๋ถ์ H ๋ ๋ฌธ์์ด์ ์ผ๋ถ๊ฐ ์๋๋ผ ๋ค์ ๋ช
๋ น์ ์ฒซ ๋ฐ์ดํธ(0x48)๋ค.
.rdata ์ ๋๋ก ๋๋๋ ๋ฌธ์์ด ์์๊ฐ ์๋๋ผ ์ฝ๋ ํ๊ฐ์ด๋ฐ ๋ฐํ ์ฆ์น๊ฐ์ด๋ผ๋ ์ ํธ๋ค.
์ด์ ์ค์ ๋ก ๋ญ๋ผ๋ ๋ฃ์ด ๋ณธ๋ค. ๋ฆฌ๋
์ค์์ PE ๋ฅผ ๋๋ฆฌ๋ ค๋ฉด wine ์ด ํ์ํ๊ณ ,
MFC ์ฑ์ด๋ผ mfc140u.dll ์ด ์๋ ํ๋ฆฌํฝ์ค๋ฅผ ๋ฐ๋ก ๋ง๋ค์ด์ผ ํ๋ค.
export WINEPREFIX="$HOME/.wine_babycmp" WINEDEBUG=-all
wineboot -i
winetricks -q -f vcrun2022์ฐฝ์ ๋์ฐ๊ณ ์ขํ๋ฅผ ์ฐ์ด ์ ๋ ฅํ๋ ๋ถ๋ถ์ ์คํฌ๋ฆฝํธ๋ก ๋ฌถ์ด ๋๋ค.
#!/usr/bin/env python3
"""babycmp.exe ๋ฅผ Xvfb ์ wine ์ผ๋ก ๋์ฐ๊ณ ์๋ฆฌ์ผ์ ์ค์ ๋ก ์
๋ ฅํด ํ๋ฉด์ ์ฐ๋๋ค.
ํฉ์ฑ์ด ์๋๋ผ ์ง์ง ์ฑ ํ๋ฉด์ด๋ค. xdotool ๋ก ์
๋ ฅ์ฐฝ์ ํด๋ฆญ โ ์๋ฆฌ์ผ ํ์ดํ โ
Check ๋ฒํผ ํด๋ฆญ๊น์ง ์ค์ ๋ง์ฐ์ค/ํค๋ณด๋ ์ด๋ฒคํธ๋ฅผ ๋ฃ๋๋ค.
python3 gui_capture.py <์๋ฆฌ์ผ> <์ถ๋ ฅPNG> [--wrong]
WM ์ด ์์ด ์ฐฝ์ ์ ๋ชฉํ์์ค์ด ์๋ค. ์ขํ๋ 1280x1040 ํ๋ฉด์ ๋ค์ด์ผ๋ก๊ทธ๊ฐ
๊ฐ์ด๋ฐ ๋จ๋ ๊ฒ์ ๊ธฐ์ค์ผ๋ก ์ก์๊ณ , ์ฐ์ ๋ค ๊ฒ์ ๋ฐฐ๊ฒฝ์ trim ํด์ ์๋ผ๋ธ๋ค.
"""
import os
import shutil
import signal
import subprocess
import sys
import time
from pathlib import Path
HERE = Path(__file__).resolve().parent
EXE = HERE / "extracted" / "babycmp.exe"
PREFIX = Path.home() / ".wine_babycmp"
SCREEN = "1280x1040x24"
EDIT_XY = (630, 543) # SERIAL ์
๋ ฅ์ฐฝ
CHECK_XY = (852, 543) # Check ๋ฒํผ
def free_display(start=141, end=180):
for n in range(start, end):
if not Path(f"/tmp/.X11-unix/X{n}").exists():
return n
raise RuntimeError("๋น ๋์คํ๋ ์ด ์์")
def sh(cmd, env, **kw):
return subprocess.run(cmd, env=env, capture_output=True, text=True, **kw)
def main():
if len(sys.argv) < 3:
print(__doc__)
return 2
serial, out = sys.argv[1], Path(sys.argv[2])
out.parent.mkdir(parents=True, exist_ok=True)
disp = f":{free_display()}"
env = dict(os.environ, DISPLAY=disp, WINEPREFIX=str(PREFIX), WINEDEBUG="-all")
xvfb = subprocess.Popen(
["Xvfb", disp, "-screen", "0", SCREEN, "-nolisten", "tcp"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
app = None
try:
time.sleep(3)
app = subprocess.Popen(["wine", str(EXE)], env=env,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
preexec_fn=os.setsid)
time.sleep(18) # mfc140u.dll ๋ก๋ฉ์ด ๋๋ฆฌ๋ค
sh(["xdotool", "mousemove", *map(str, EDIT_XY), "click", "1"], env)
time.sleep(0.6)
sh(["xdotool", "type", "--delay", "40", serial], env)
time.sleep(1.0)
sh(["xdotool", "mousemove", *map(str, CHECK_XY), "click", "1"], env)
time.sleep(2.5)
raw = out.with_suffix(".raw.png")
sh(["import", "-window", "root", str(raw)], env)
subprocess.run(["convert", str(raw), "-bordercolor", "black", "-fuzz", "2%",
"-trim", "+repage", "-bordercolor", "#0d1117",
"-border", "28", str(out)], check=True)
raw.unlink(missing_ok=True)
print(f"SAVED: {out}")
finally:
if app is not None:
try:
os.killpg(os.getpgid(app.pid), signal.SIGKILL)
except ProcessLookupError:
pass
# ์ด ํ๋ฆฌํฝ์ค ์ ์ฉ wineserver ๋ง ๋ด๋ฆฐ๋ค (๋ค๋ฅธ wine ์์
๋ณดํธ)
if shutil.which("wineserver"):
sh(["wineserver", "-k"], env)
xvfb.terminate()
xvfb.wait(timeout=10)
return 0
if __name__ == "__main__":
sys.exit(main())python3 gui_capture.py 'DH{aaaaaaaaaaaaaaaaaaaa}' /home/jinho/homepage/public/images/blog/dreamhack-babycmp-writeup/04_gui_wrong.png
ํ๋ฉด์ ์ ์งํ๋ค. ๋ผ๋ฒจ ํ๋, ์
๋ ฅ์นธ ํ๋, ๋ฒํผ ํ๋. ํ๋ฆฌ๋ฉด opps! try again!.
์ ๋ต ํ๋๋ฅผ ๋งํ๋ ๊ฒ ๋ง๊ณ ๋ค๋ฅธ ๊ธธ์ ์๋ค๋ ๊ฒ ๋์ผ๋ก ํ์ธ๋๋ค.
๐ง ๊ด๋ฌธ โ ๊ฒ์ฌ ํจ์๊ฐ ๋จผ์ ๋ณด๋ ์ธ ๊ฐ์ง
.text ๊ฐ 11KB ๋ฟ์ด๋ผ ๋์ค์ด์
๋ธ๋ฆฌ๋ฅผ ํต์งธ๋ก ํ์ด๋ ๋ถ๋ด์ด ์๋ค.
0x140001c30 ๋ถํฐ ์์ํ๋ ํจ์ ํ๋๊ฐ ๋ฌธ์์ด ๊ฐ์ฒด๋ฅผ ๋ฐ์ bool ์ ๋๋ ค์ฃผ๋ ๋ชจ์์ธ๋ฐ,
๊ทธ ์์ด ๊ณง ๊ฒ์ฌ ๋ฃจํด์ด๋ค.
์ฃผ์์ sed ๊ท์น ํ์ผ๋ก ์น์๋ค. --cmd ์์ ์ธ๋ผ์ธ์ผ๋ก sed 's|โฆ|โฆ|' ๋ฅผ ์ฐ๋ฉด
๋ฐ์ดํ๊ฐ ์
ธ์ ์ง๋๋ฉฐ ๊นจ์ ธ์, ์นํ ๊ท์น์ ํ์ผ์ ๋ฐ๋ก ๋๋ค.
s|^ 140001c9d:.*|& <-- rcx+0x10 = std::string ์ size()|
s|^ 140001ca1:.*|& <-- ๊ธธ์ด๊ฐ 0x18(=24)์ด ์๋๋ฉด ๋ฐ๋ก ํ๋ฝ|
s|^ 140001cd3:.*|& <-- rbp+0xf ์ ๋ง๋ค์ด ๋ 3๊ธ์ ๋ฌธ์์ด|
s|^ 140001cdb:.*|& <-- ์ ๋์ฌ๋ฅผ ํ ๊ธ์์ฉ ๋์กฐ|
s|^ 140001ced:.*|& <-- r11 = 3, ์ธ ๊ธ์๋ง ๋ณธ๋ค|
s|^ 140001d02:.*|& <-- 24๋ฒ์งธ(0x17) ๊ธ์๋ 0x7d = '}'|objdump -d -M intel --start-address=0x140001c9d --stop-address=0x140001d08 extracted/babycmp.exe | sed -f gate.sed | tail -n +5
๊ด๋ฌธ์ด ์ ์ด๋ค.
๊ธธ์ด๋ ์ ํํ 24๋ฐ์ดํธ. mov r15, [rcx+0x10] ๋ก std::string::size() ๋ฅผ ์ฝ๊ณ
cmp r15, 0x18 ๋ก ๋น๊ตํ๋ค. jne ๊ฐ ์๋๋ผ je ๋ก ํต๊ณผ ๊ฒฝ๋ก๋ฅผ ์ก์ ๋จ์ ๋ฟ ์๋ฏธ๋ ๊ฐ๋ค.
์ ์ธ ๊ธ์๋ ๊ณ ์ ๋ฌธ์์ด. rbp+0xf ์ ์ธ ๊ธ์์ง๋ฆฌ ์์ ๋ฌธ์์ด์ ๋ง๋ค์ด ๋๊ณ
(r13=0xf ๊ฐ capacity, r11=3 ์ด size) ํ ๊ธ์์ฉ cmp ํ๋ค.
๊ทธ ์ธ ๊ธ์๋ .rdata 0x140004f50 ์์ WORD ํ๋ + BYTE ํ๋๋ก ๋ณต์ฌํด ์ค๋๋ฐ,
xxd -s 0x4150 -l 8 extracted/babycmp.exe์ฝ์ด ๋ณด๋ฉด 44 48 7b, ์ฆ DH{ ๋ค. ํ๋๊ทธ ํ์์ด ๊ณง ์๋ฆฌ์ผ ํ์์ด๋ค.
๋ง์ง๋ง ๊ธ์๋ ๋ซ๋ ์ค๊ดํธ. cmp BYTE PTR [rax+0x17], 0x7d.
0x17 ์ 23์ด๊ณ , 24๊ธ์ ๋ฌธ์์ด์ ๋ง์ง๋ง ์ธ๋ฑ์ค๋ค.
์ฌ๊ธฐ๊น์ง๋ก ํํ๊ฐ ์ ํด์ง๋ค. DH{ + 20๊ธ์ + }. ๋จ์ ๊ฑด ๊ฐ์ด๋ฐ 20๊ธ์๋ค.
๐ฃ ํต์ฌ โ ํ ๊ธ์๋ฅผ int32 ๋ก ๋ถํ๋ฆฌ๋ ๋ ์ค
๊ด๋ฌธ์ ํต๊ณผํ๋ฉด 0x140001a10 ์ด ํธ์ถ๋๋ค. ์ธ์๋ ์
๋ ฅ ๋ฌธ์์ด, ๋ฐํ์ std::vector<int>.
๊ธ์ ์๋งํผ int32 ๋ฅผ ๋ด๋ ๋ฒกํฐ๋ฅผ ๋ง๋ค์ด ๋๋ ค์ฃผ๋ ํจ์๋ค.
s|^ 140001af5:.*|& <-- ํค "neko_hat" ์ ์ฝ๋ ์ฆ์น๊ฐ์ผ๋ก ์คํ์|
s|^ 140001b0b:.*|& <-- i < size() ์ธ ๋์ ๋ฐ๋ณต|
s|^ 140001b34:.*|& <-- v = (int8)serial[i] ๋ถํธํ์ฅ ์ฃผ์|
s|^ 140001b44:.*|& <-- v = ror32(v, 4)|
s|^ 140001b52:.*|& <-- eax = i % 8|
s|^ 140001b67:.*|& <-- v ^= key[i % 8]|
s|^ 140001b73:.*|& <-- eax = i % 4|
s|^ 140001b89:.*|& <-- v += key[i % 4]|
s|^ 140001b8f:.*|& <-- v += 0x7530 = 30000|objdump -d -M intel --start-address=0x140001af5 --stop-address=0x140001ba9 extracted/babycmp.exe | sed -f loop.sed | tail -n +5
๋ฃจํ์ ๋ค์ด๊ฐ๊ธฐ ์ ๋ ์ค์ด ๋จผ์ ๋์ ๋ค์ด์จ๋ค.
movabs rax, 0x7461685f6f6b656e ; "neko_hat" ์ ๋ฆฌํ์๋์ธ 8๋ฐ์ดํธ๋ก
mov QWORD PTR [rsp+0x38], rax
mov BYTE PTR [rsp+0x40], 0 ; ๋ ์ข
๋ฃํค๋ .rdata ์ ์๋ค. movabs ์ ์ฆ์น๊ฐ์ผ๋ก ์ฝ๋์ ๋ฐํ ์๋ค๊ฐ ์คํ์ ๋ณต์ฌ๋๋ค.
์๊น strings ๊ฐ neko_hatH ๋ก ๋ณด์ฌ ์ค ์ด์ ๊ฐ ์ด๊ฒ์ด๋ค.
.rdata ๋ง ๋ค์ง๊ฑฐ๋, strings ๊ฒฐ๊ณผ๋ฅผ ๋ฌธ์์ด ์์ ๋ชฉ๋ก์ผ๋ก๋ง ์ฝ์ผ๋ฉด ์ฌ๊ธฐ์ ํ ๋ฒ ๋งํ๋ค.
๋ฃจํ ๋ณธ์ฒด๋ ๋ ์ค๋ก ์์ฝ๋๋ค.
for (int i = 0; i < s.size(); i++) {
int v = (signed char)s[i]; // movsx โ ๋ถํธํ์ฅ
v = _rotr(v, 4); // ror eax, 4
v ^= key[i % 8]; // xor
v += key[i % 4]; // add
v += 30000; // add 0x7530
out[i] = v;
}i % 8 ๊ณผ i % 4 ๋ ์์ค์ ๋๋จธ์ง ์ฐ์ฐ์ผ๋ก ์ฐ์ฌ ์์์ ํ
๋ฐ, ์ปดํ์ผ๋ฌ๊ฐ
and eax, 0x80000007 + ์์ ๋ณด์ (dec / or / inc)์ผ๋ก ํผ์ณ ๋จ๋ค.
์
๋ ฅ ์ธ๋ฑ์ค๋ ํญ์ 0 ์ด์์ด๋ผ ๋ณด์ ๊ฒฝ๋ก๋ ์ค์ ๋ก ์ ํ๋ค. ๊ทธ๋ฅ i % 8 ๋ก ์ฝ์ผ๋ฉด ๋๋ค.
ํต์ฌ์ ror ๋ค. ์ค๋ฅธ์ชฝ์ผ๋ก 4๋นํธ ๋๋ฆฌ๋ฉด ์๋ ๊ฐ์ ํ์ ๋๋ธ์ด ์ต์์๋ก ์ฌ๋ผ๊ฐ๋ค.
๊ธ์ ํ๋๊ฐ 0x00~0x7f ๋ฒ์๋ผ, ๊ฒฐ๊ณผ๋ ์์ 4๋นํธ์ ๊ธ์์ ํ์ ๋๋ธ์ด ๋ฐํ๊ณ
๋๋จธ์ง๋ 30000 ๊ทผ์ฒ์ ์์ ์๊ฐ ๋๋ ๋ชจ์์ด ๋๋ค.

์ฒซ ๊ธ์ D(0x44)๋ก ์๊ณ์ฐํด ๋ณด๋ฉด 0x44 โ 0x40000004 โ 0x4000006a โ 0x400000d8 โ
0x40007608. ๋ค์์ ํ์ธํ๊ฒ ์ง๋ง ์ด ๊ฐ์ด .data ์ 0๋ฒ์งธ ๊ธฐ๋๊ฐ๊ณผ ์ ํํ ๊ฐ๋ค.
์ด ๊ตฌ์กฐ ๋๋ฌธ์ ์๋ฆฌ๋ง๋ค ํ๋ณด๊ฐ ํ๋๋ก ๋จ์ด์ง๋ค. ๊ฐ ์๋ฆฌ์ ๊ณ์ฐ์ด ์๋ค ๊ธ์์ ์ ํ ์ฝํ์ง ์์์(๋ธ๋ก ์ฒด์ด๋๋, ๋์ ์ํ๋ ์๋ค) ์ธ์ ๊ฐ๋ฅํ 95๊ธ์๋ฅผ ํ ๋ฒ์ฉ ๋ฃ์ด ๋ณด๋ฉด ๋์ด๋ค.
๐งฎ ๊ธฐ๋๊ฐ 24๊ฐ๋ .data ์ ๊ทธ๋๋ก
๋ณํ์ด ๋๋๋ฉด ๊ฒฐ๊ณผ ๋ฒกํฐ๋ฅผ ์์ ๋ฐฐ์ด๊ณผ ๋น๊ตํ๋ค.
objdump -d -M intel --start-address=0x140001dc7 --stop-address=0x140001df6 extracted/babycmp.exe | tail -n +8; xxd -s 0x7050 -l 96 extracted/babycmp.exe
๋น๊ต ๋ฃจํ๊ฐ ์ฌ๋ฏธ์๋ค. int ๋จ์๊ฐ ์๋๋ผ QWORD ๋จ์๋ก 8๋ฐ์ดํธ์ฉ ํ๋๋ค.
lea rcx, [rip+0x6282] ; 0x140008050 โ ๊ธฐ๋๊ฐ ๋ฐฐ์ด ์์
mov r8, [rbp-0x39] ; ๋ฐฉ๊ธ ๋ง๋ ๋ฒกํฐ์ ๋ฐ์ดํฐ ํฌ์ธํฐ
mov rdx, r8
sub rdx, rcx ; ๋ ๋ฐฐ์ด์ ๊ฑฐ๋ฆฌ
lea r9, [rip+0x62d1] ; 0x1400080b0 โ ๊ธฐ๋๊ฐ ๋ฐฐ์ด ๋
...
mov rax, [rcx]
cmp [rdx+rcx*1], rax ; 8๋ฐ์ดํธ์ฉ ๋น๊ต
jne <์คํจ>
add rcx, 8
cmp rcx, r9
jl <๊ณ์>0x1400080b0 - 0x140008050 = 0x60, 96๋ฐ์ดํธ๋ค. int32 24๊ฐ.
๊ธธ์ด ๊ฒ์ฌ๊ฐ 24์๋ ๊ฒ๊ณผ ์ ํํ ๋ง๋๋ค.
xxd ๋ก ๋ฝ์ ๋ฐ์ดํธ๋ฅผ ๋ณด๋ฉด ๊ท์น์ด ๋์ ๋๋ค. 4๋ฐ์ดํธ๋ง๋ค ์ธ ๋ฒ์งธ ๋ฐ์ดํธ๊ฐ 75 ๋๋ 76 ์ด๋ค.
๋ฆฌํ์๋์ธ์ด๋ ์ด๊ฒ 0x7530(30000) ๊ทผ์ฒ๋ผ๋ ๋ป์ด๊ณ , ๋ง์ง๋ง add ๊ฐ ๋จ๊ธด ์๊ตญ์ด๋ค.
์์ ๋ฐ์ดํธ๋ 40, 80, b0, 90, 00, 50โฆ ์ผ๋ก ํฉ์ด์ง๋๋ฐ ์ด๊ฒ ror ์ด ์ฌ๋ ค ๋ณด๋ธ ๋๋ธ์ด๋ค.
ํ์ผ ์คํ์
์ .data ์ VMA 0x140008000 ์ด ํ์ผ 0x7000 ์ ๋์ด๋ฏ๋ก
0x7000 + 0x50 = 0x7050. ์ ๋ช
๋ น์ -s 0x7050 ์ด ๊ทธ๋ ๊ฒ ๋์จ ๊ฐ์ด๋ค.
๐ฏ ์ญ์ฐ โ ์๋ฆฌ๋ง๋ค 95๊ธ์๋ฅผ ๋ฃ์ด ๋ณธ๋ค
๋๋๋ฆฌ๋ ํจ์๋ฅผ ๋ฐ๋ก ์ ๋ํ ํ์๊ฐ ์๋ค. ์๋ฆฌ ํ๋์ ํ๋ณด๊ฐ 95๊ฐ๋ฟ์ด๋ ์ ๋ฐฉํฅ์ผ๋ก ๊ณ์ฐํด์ ๋ง๋ ๊ฒ์ ๊ณ ๋ฅด๋ฉด ๋๋ค. ํ๋ณด๊ฐ ์ ํํ ํ๋์ฉ ๋์ค๋์ง๋ ๊ฐ์ด ํ์ธํ๋ค.
์ฃผ์ยทํคยท๊ธฐ๋๊ฐ์ ์ ๋ถ ๋ฐ์ด๋๋ฆฌ์์ ์ง์ ์ฝ๋๋ค. ํ๋์ฝ๋ฉํ ์์๋ฅผ ๋๋ฉด "์ด ๊ฐ์ด ์ด๋์ ์๋"๊ฐ ๊ธ ๋ฐ์ผ๋ก ์๊ธฐ ๋๋ฌธ์ด๋ค.
#!/usr/bin/env python3
"""babycmp (DreamHack, Silver 2, reversing) โ solver
babycmp.exe ์ ๊ฒ์ฆ ๋ฃจํด์ ๊ทธ๋๋ก ๋ค์ ๊ตฌํํ๊ณ , ๋ฐ์ด๋๋ฆฌ์์ ๋ฝ์
๊ธฐ๋๊ฐ ๋ฐฐ์ด๊ณผ ๋ง๋ ๋ฌธ์๋ฅผ ์๋ฆฌ๋ง๋ค ์ฐพ์ ์๋ฆฌ์ผ(=ํ๋๊ทธ)์ ๋ณต์ํ๋ค.
๊ฒ์ฆ ๋ฃจํด (0x140001a10, 0x140001c30):
if (len(s) != 0x18) return false
if (s[0:3] != "DH{") return false
if (s[0x17] != '}') return false
for i in range(len(s)):
v = (int32)(int8)s[i]
v = ror32(v, 4)
v ^= key[i % 8] # key = "neko_hat"
v += key[i % 4]
v += 30000 # 0x7530
if v != expected[i]: return false
๊ธฐ๋๊ฐ 24๊ฐ๋ .data 0x140008050 ์, ์ ๋์ฌ "DH{" ๋ .rdata 0x140004f50 ์ ์๋ค.
๋ฐ์ด๋๋ฆฌ์์ ์ง์ ์ฝ์ผ๋ฏ๋ก ํ๋์ฝ๋ฉํ ๊ฐ์ด ์๋ค.
"""
import struct
import sys
from pathlib import Path
EXE = Path(__file__).with_name("extracted") / "babycmp.exe"
# objdump -h babycmp.exe ๋ก ์ป์ (์น์
, VMA, ํ์ผ์คํ์
, ํฌ๊ธฐ)
SECTIONS = [
(".text", 0x140001000, 0x00400, 0x2D5C),
(".rdata", 0x140004000, 0x03200, 0x3D84),
(".data", 0x140008000, 0x07000, 0x0400),
]
KEY = b"neko_hat" # movabs rax, 0x7461685f6f6b656e @ 0x140001af5
EXPECTED_RVA = 0x140008050 # lea rcx,[rip+0x6282]
EXPECTED_END = 0x1400080B0 # lea r9,[rip+0x62d1]
PREFIX_RVA = 0x140004F50 # movzx eax,WORD PTR [rip+0x32c5]
SERIAL_LEN = 0x18 # cmp r15,0x18
def rva_to_offset(rva: int) -> int:
for _name, vma, off, size in SECTIONS:
if vma <= rva < vma + size:
return off + (rva - vma)
raise ValueError(f"RVA {rva:#x} ๊ฐ ์ด๋ ์น์
์๋ ์๋ค")
def ror32(value: int, count: int) -> int:
value &= 0xFFFFFFFF
count &= 31
return ((value >> count) | (value << (32 - count))) & 0xFFFFFFFF
def transform(ch: int, i: int) -> int:
"""๊ฒ์ฆ ๋ฃจํด ํ ๋ฌธ์๋ถ. ์
๋ ฅ์ movsx ๋ก ๋ถํธํ์ฅ๋ int32 ๋ก ๋ค๋ค์ง๋ค."""
v = ch - 0x100 if ch >= 0x80 else ch # movsx eax, byte
v = ror32(v, 4)
v ^= KEY[i % 8]
v = (v + KEY[i % 4]) & 0xFFFFFFFF
v = (v + 30000) & 0xFFFFFFFF
return v
def main() -> int:
blob = EXE.read_bytes()
off = rva_to_offset(PREFIX_RVA)
prefix = blob[off:off + 3]
off = rva_to_offset(EXPECTED_RVA)
count = (EXPECTED_END - EXPECTED_RVA) // 4
expected = struct.unpack(f"<{count}I", blob[off:off + count * 4])
print(f"[*] ์ ๋์ฌ : {prefix.decode()}")
print(f"[*] ์๋ฆฌ์ผ ๊ธธ์ด : {SERIAL_LEN}")
print(f"[*] ๊ธฐ๋๊ฐ ๊ฐ์ : {count}")
serial = bytearray()
for i, target in enumerate(expected):
hits = [c for c in range(0x20, 0x7F) if transform(c, i) == target]
if len(hits) != 1:
print(f"[!] index {i}: ํ๋ณด {len(hits)}๊ฐ {hits} โ ์ค๋จ")
return 1
serial.append(hits[0])
flag = serial.decode()
print(f"[*] ๋ณต์๋ ์๋ฆฌ์ผ: {flag}")
ok = (
len(serial) == SERIAL_LEN
and serial[:3] == prefix
and serial[SERIAL_LEN - 1] == ord("}")
and all(transform(c, i) == expected[i] for i, c in enumerate(serial))
)
print(f"[*] ์์ฒด ๊ฒ์ฆ : {'PASS' if ok else 'FAIL'}")
print()
print(f"FLAG: {flag}")
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())python3 solve.py
24์๋ฆฌ ๋ชจ๋ ํ๋ณด๊ฐ ์ ํํ ํ๋์ฉ ๋์๋ค. len(hits) != 1 ์์ ํ ๋ฒ๋ ์ ๊ฑธ๋ ธ๋ค๋ ๋ป์ด๋ผ
ํ๋๊ทธ๊ฐ ์ ์ผํด๋ผ๋ ๊ฒ๋ ๊ฐ์ด ์ฆ๋ช
๋๋ค.
๐ ๊ฒ์ฆ โ ๋ฐ์ด๋๋ฆฌ์ ๊ธฐ๊ณ์ด๋ฅผ ๊ทธ๋๋ก ๋๋ ค ๋ณธ๋ค
์ฌ๊ธฐ์ ๋ฉ์ถ๋ฉด ์ฐ์ฐํ๋ค. ์ ํ์ด์ฌ์ ์ด๋๊น์ง๋ ๋ด๊ฐ ์ด์
๋ธ๋ฆฌ๋ฅผ ์ฝ๊ณ ์์ผ๋ก ์ฎ๊ธด ๊ฒ์ด๋ค.
ror ์ rol ๋ก ์ฝ์๊ฑฐ๋, movsx ๋ฅผ movzx ๋ก ๋ดค๊ฑฐ๋, XOR ๊ณผ ADD ์ ์์๋ฅผ ๋ฐ๊ฟจ๋ค๋ฉด
๊ฐ์ด ์ ๋ง์์ ๋ฐ๋ก ํฐ๊ฐ ๋๊ฒ ์ง๋ง, ๋ฐ๋๋ก ์ฐ์ฐํ ๋ง์๋จ์ด์ง๋ ์ค์๋ ์์ ์ ์๋ค.
๊ทธ๋์ ๋ฐ์ด๋๋ฆฌ์์ .text ๋ฅผ ๋ ๋ค๊ฐ ๊ทธ ๋ฃจํ๋ฅผ Unicorn ์ผ๋ก ๊ทธ๋๋ก ์คํํ๋ค.
์ง์ ์ง ์ฝ๋๊ฐ ์๋๋ผ ์ถ์ ์๊ฐ ์ปดํ์ผํ ๊ธฐ๊ณ์ด๊ฐ ๋๋ ๊ฒ์ด๋ผ, ์ฝ๊ธฐ ์ค์๊ฐ ์์ผ๋ฉด ์ฌ๊ธฐ์ ๊ฐ๋ฆฐ๋ค.
๋ฃจํ๊ฐ ์ฐธ์กฐํ๋ ๊ฑด ์
๋ฟ์ด๋ค. rdi ์ std::string, rsi ์ std::vector, ๊ทธ๋ฆฌ๊ณ
rsp+0x38 ์ ํค. ์ด ์
๋ง ๋ฉ๋ชจ๋ฆฌ์ ๋ง๋ค์ด ์ฃผ๋ฉด 0x140001b09 ๋ถํฐ 0x140001ba9 ๊น์ง
๊ทธ๋๋ก ๋๋ฆด ์ ์๋ค.
โถ๐ ์ฝ์ง โ std::string ์ SSO ๋ก ์ก์๋ค๊ฐ 16๊ธ์๊น์ง๋ง ๋ง์๋ค
์ฒซ ํ์ capacity ๋ฅผ 0x0f ๋ก ์คฌ๋ค. MSVC ์ std::string ์ ์งง์ ๋ฌธ์์ด์ ํ ๋์
๊ฐ์ฒด ์์ ๊ทธ๋๋ก ๋ด๋๋ฐ(SSO), ๋ฃจํ๋ cmp QWORD PTR [rdi+0x18], 0x10 ๋ก ๊ทธ ๋ถ๊ธฐ๋ฅผ ํ๋ค.
0x10 ๋ฏธ๋ง์ด๋ฉด r8 = rdi, ์ฆ ๊ฐ์ฒด ์์ ์ด ๋ฌธ์ ๋ฒํผ๋ค.
๋ฌธ์ ๋ ๊ฐ์ ๊ฐ์ฒด์ +0x10 ์ size ๋ฅผ ์จ ๋ฃ์ด์ผ ํ๋ค๋ ๊ฒ์ด๋ค.
๋ฌธ์ ๋ฒํผ๊ฐ ๊ฐ์ฒด ์์๋ถํฐ ๊น๋ฆฌ๋ 17๋ฒ์งธ ๊ธ์(์ธ๋ฑ์ค 16)๊ฐ size ํ๋์ ๊ทธ๋๋ก ๋ฎ์ธ๋ค.
# โ
์ฌ๊ธฐ๊ฐ ํ๋ ธ๋ค โ capacity 0x0f ๋ SSO ๊ฒฝ๋ก๋ผ rdi ๊ฐ ๊ณง ๋ฌธ์ ๋ฒํผ๋ค.
# ๋ฐ๋ก ์๋ size ๋ฅผ rdi+0x10 ์ ์ฐ๋ ์๊ฐ 17๋ฒ์งธ ๊ธ์๊ฐ ๋ ์๊ฐ๋ค.
uc.mem_write(STR_OBJ, serial)
uc.mem_write(STR_OBJ + 0x10, struct.pack("<Q", len(serial)))
uc.mem_write(STR_OBJ + 0x18, struct.pack("<Q", 0x0F))python3 emulate_sso.py 'DH{y0u_4r3_cmp__ma5t3r!}'
์ฆ์์ด ์์ฃผ ์์ ํด์ ์คํ๋ ค ํท๊ฐ๋ ธ๋ค. ์ 16๊ฐ๊ฐ ๋ฑ ๋ง์ผ๋ ๋ณํ์์ ๋ง์ ๊ฒ ๊ฐ์๋ฐ
๋ค๋ง ํ๋ฆฌ๋, ์ฒ์์ i % 8 ์ฃผ๊ธฐ๋ ๋ถํธํ์ฅ ์ชฝ์ ์์ฌํ๋ค.
์ค์ ๋ก๋ ๋ณํ์์ด ์๋๋ผ ๋ด๊ฐ ๋ง๋ ๊ฐ์ง ๊ฐ์ฒด์ ๋ ์ด์์์ด ํ๋ฆฐ ๊ฒ์ด์๋ค.
์ธ๋ฑ์ค 16์ ๊ฒฐ๊ณผ๋ฅผ ์ญ์ผ๋ก ํ๋ฉด ๊ธ์๊ฐ 0x18(=24), ์ฆ size ๊ฐ์ด๊ณ
17๋ฒ๋ถํฐ๋ ์ ๋ถ 0x00 ์ด์๋ค. ๊ฐ์ด "์ด์ํ ๊ฐ"์ด ์๋๋ผ "๋ด๊ฐ ๋ฐฉ๊ธ ์จ ๋ฃ์ ๊ฐ"์ด๋ฉด
์ฝ๊ธฐ๊ฐ ํ๋ฆฐ ๊ฒ ์๋๋ผ ์ธํ
์ด ํ๋ฆฐ ๊ฒ์ด๋ค.
24๊ธ์๋ ์ค์ ๋ก๋ ํ์ ์กํ๋ฏ๋ก capacity ๋ฅผ 0x1f ๋ก ์ฌ๋ฆฌ๊ณ ๋ฐ์ดํฐ ํฌ์ธํฐ๋ฅผ
๋ฐ๋ก ๋๋ ๊ทธ๋๋ก ๋ง์๋ค. ๋ง์ง๋ง ํ๋กฌํํธ์ ๋ถ์ ํ์๋ ์คํฌ๋ฆฝํธ๊ฐ ๋ถ์ผ์น์ผ ๋
1๋ก ๋๋๊ฒ ํด ๋ ๊ฒ์ด๋ผ ์ ์์ด๋ค.
๊ณ ์น ํ์ ์ด๋ ๋ค.
#!/usr/bin/env python3
"""babycmp.exe ์ ๋ณํ ๋ฃจํ๋ฅผ Unicorn ์ผ๋ก '๊ทธ๋๋ก' ๋๋ ค ๋ณธ๋ค.
์์ผ๋ก ์ฎ๊ฒจ ์ ์ ํ์ด์ฌ ์ฌ๊ตฌํ(solve.py)์ด ๋ง๋์ง ํ์ธํ๋ ์ฉ๋๋ค.
์ง์ ์ง ์ฝ๋๊ฐ ์๋๋ผ ๋ฐ์ด๋๋ฆฌ์์ ๋ ๋ธ .text ๋ฐ์ดํธ๋ฅผ ๊ทธ๋๋ก ์คํํ๋ฏ๋ก,
ror/xor/add ์์๋ ๋ถํธํ์ฅ์ ์๋ชป ์ฝ์์ผ๋ฉด ์ฌ๊ธฐ์ ๊ฐ์ด ์ด๊ธ๋๋ค.
python3 emulate.py 'DH{y0u_4r3_cmp__ma5t3r!}'
๋ฃจํ ๊ตฌ๊ฐ์ 0x140001b09(์ด๊ธฐ ๊ฒฝ๊ณ๊ฒ์ฌ) ~ 0x140001ba9(๋ฃจํ ํ์ถ ์งํ).
๋ฃจํ๊ฐ ์ฐธ์กฐํ๋ ๊ฒ์ ์
๋ฟ์ด๋ผ ๊ทธ ์
๋ง ๋ง๋ค์ด ์ฃผ๋ฉด ๋๋ค.
rdi : std::string โ [rdi+0x10]=size, [rdi+0x18]=capacity, [rdi]=ํ ๋ฐ์ดํฐ ํฌ์ธํฐ
rsi : std::vector โ [rsi]=int32 ๋ฐฐ์ด ์์ ํฌ์ธํฐ
rsp+0x38 : ํค 8๋ฐ์ดํธ "neko_hat"
capacity ๋ฅผ 0x10 ๋ฏธ๋ง์ผ๋ก ์ฃผ๋ฉด MSVC ์ SSO ๊ท์ฝ๋๋ก rdi ์์ฒด๋ฅผ ๋ฌธ์ ๋ฒํผ๋ก ์ฝ๋๋ฐ,
๊ทธ๋ฌ๋ฉด ์คํ์
0x10 ์ size ํ๋๊ฐ 17๋ฒ์งธ ๋ฌธ์์ ๊ฒน์ณ 16๋ฐ์ดํธ๊น์ง๋ง ๋ง๋๋ค.
24๊ธ์ ์๋ฆฌ์ผ์ ์ค์ ๋ก๋ ํ์ ์กํ๋ฏ๋ก capacity ๋ฅผ 0x1f ๋ก ๋๋ค.
"""
import struct
import sys
from pathlib import Path
from unicorn import Uc, UC_ARCH_X86, UC_MODE_64
from unicorn.x86_const import (UC_X86_REG_RSP, UC_X86_REG_RBP, UC_X86_REG_RSI,
UC_X86_REG_RDI)
HERE = Path(__file__).resolve().parent
EXE = HERE / "extracted" / "babycmp.exe"
IMAGE_BASE = 0x140000000
SECTIONS = [(".text", 0x140001000, 0x00400, 0x2D5C),
(".rdata", 0x140004000, 0x03200, 0x3D84),
(".data", 0x140008000, 0x07000, 0x0400)]
LOOP_START = 0x140001B09
LOOP_END = 0x140001BA9
KEY_RVA = 0x140001AF5 # movabs rax, <key> โ ์ฆ์น๊ฐ์์ ํค๋ฅผ ์ง์ ๋ฝ๋๋ค
EXPECTED_RVA = 0x140008050
EXPECTED_END = 0x1400080B0
STACK = 0x00200000
HEAP = 0x00300000
STR_OBJ = HEAP + 0x000 # std::string
STR_DAT = HEAP + 0x080 # ๊ทธ ๋ฌธ์์ด์ด ๊ฐ๋ฆฌํค๋ ํ ๋ฒํผ
VEC_OBJ = HEAP + 0x100 # std::vector
OUT_BUF = HEAP + 0x200 # int32 ๊ฒฐ๊ณผ ๋ฐฐ์ด
def main() -> int:
serial = (sys.argv[1] if len(sys.argv) > 1 else "DH{y0u_4r3_cmp__ma5t3r!}").encode()
blob = EXE.read_bytes()
uc = Uc(UC_ARCH_X86, UC_MODE_64)
uc.mem_map(IMAGE_BASE, 0x20000)
uc.mem_map(STACK, 0x10000)
uc.mem_map(HEAP, 0x10000)
for _name, vma, off, size in SECTIONS:
uc.mem_write(vma, blob[off:off + size])
# ํค๋ ์ฝ๋ ์ movabs ์ฆ์น๊ฐ(0x48 0xB8 + imm64)์์ ์ฝ๋๋ค
key = uc.mem_read(KEY_RVA + 2, 8)
print(f"[*] ์ฝ๋์์ ๋ฝ์ ํค : {bytes(key).decode()} (movabs @ {KEY_RVA:#x})")
# std::string โ capacity >= 0x10 ์ด๋ผ ๋ฃจํ๊ฐ [rdi] ์ ํ ํฌ์ธํฐ๋ฅผ ๋ฐ๋ผ๊ฐ๋ค
uc.mem_write(STR_DAT, serial)
uc.mem_write(STR_OBJ, struct.pack("<Q", STR_DAT))
uc.mem_write(STR_OBJ + 0x10, struct.pack("<Q", len(serial)))
uc.mem_write(STR_OBJ + 0x18, struct.pack("<Q", 0x1F))
# std::vector โ ๋ฐ์ดํฐ ํฌ์ธํฐ๋ง ์์ผ๋ฉด ๋๋ค
uc.mem_write(VEC_OBJ, struct.pack("<Q", OUT_BUF))
rsp = STACK + 0x8000
uc.reg_write(UC_X86_REG_RSP, rsp)
uc.mem_write(rsp + 0x38, bytes(key))
uc.reg_write(UC_X86_REG_RBP, 0) # edx=0, r9=0 ์ ๋ง๋๋ ์์ค ๋ ์ง์คํฐ
uc.reg_write(UC_X86_REG_RSI, VEC_OBJ)
uc.reg_write(UC_X86_REG_RDI, STR_OBJ)
print(f"[*] ์
๋ ฅ ์๋ฆฌ์ผ : {serial.decode()} ({len(serial)}๋ฐ์ดํธ)")
print(f"[*] ์๋ฎฌ๋ ์ด์
๊ตฌ๊ฐ : {LOOP_START:#x} โ {LOOP_END:#x}")
uc.emu_start(LOOP_START, LOOP_END)
got = struct.unpack(f"<{len(serial)}i",
uc.mem_read(OUT_BUF, len(serial) * 4))
n = (EXPECTED_END - EXPECTED_RVA) // 4
want = struct.unpack(f"<{n}i", uc.mem_read(EXPECTED_RVA, n * 4))
print()
print(" idx ch ์๋ฎฌ๋ ์ด์
๊ฒฐ๊ณผ .data ๊ธฐ๋๊ฐ ์ผ์น")
for i, (g, w) in enumerate(zip(got, want)):
print(f" {i:3d} {chr(serial[i])!r:>4} {g:12d} {w:12d} "
f"{'O' if g == w else 'X'}")
ok = list(got) == list(want)
print()
print(f"RESULT: {'MATCH โ ์ฌ๊ตฌํ์ด ๋ฐ์ด๋๋ฆฌ์ ๋์ผํ๋ค' if ok else 'MISMATCH'}")
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())python3 emulate.py 'DH{y0u_4r3_cmp__ma5t3r!}'
24๊ฐ ์ ๋ถ ์ผ์น. ํค๋ .rdata ๋ฅผ ๋ค์ง ๊ฒ ์๋๋ผ movabs ์ฆ์น๊ฐ์์ ์ฝ์ ๊ฒ์ด๋ผ
"์ฝ๋ ์์ ๋ฐํ ์๋ค"๋ ์ฃผ์ฅ๊น์ง ๊ฐ์ด ํ์ธ๋๋ค.
๐ ์ค์ ์ฑ์ ๋ฃ์ด ๋ณธ๋ค
๋ง์ง๋ง์ ์๋ณธ ํ๋ก๊ทธ๋จ์ด๋ค. ์๊น ์๋ฌด ๊ฐ์ด๋ ๋ฃ์ด opps! try again! ์ ๋ฐ์๋ ๊ทธ ํ๋ฉด์
๋ณต์ํ ์๋ฆฌ์ผ์ ๋ฃ๋๋ค. ์คํฌ๋ฆฝํธ๋ ์ ์ฐฐ ๋ ์ด ๊ฒ๊ณผ ๊ฐ๊ณ ์ธ์๋ง ๋ฐ๋๋ค.
python3 gui_capture.py 'DH{y0u_4r3_cmp__ma5t3r!}' /home/jinho/homepage/public/images/blog/dreamhack-babycmp-writeup/11_gui_success.png
Congraturations!. ์
๋ ฅ์นธ์ด ์ข์ ์๋ฆฌ์ผ ๋ท๋ถ๋ถ์ด ์๋ ค ๋ณด์ด์ง๋ง, ์์ solve.py ์ถ๋ ฅ๊ณผ
๊ฐ์ ๊ฐ์ ๊ทธ๋๋ก ํ์ดํํ ๊ฒ์ด๋ค.
๐งฐ ์ฌํ
๋น๋ํ ๊ฒ ์์ด์ ์ค๋น๋ฌผ์ ํ์ด์ฌ ํ๋๋ฟ์ด๋ค. ๊ต์ฐจ๊ฒ์ฆ๊น์ง ํ๋ ค๋ฉด Unicorn ์ด ํ์ํ๋ค.
| ํญ๋ชฉ | ๊ฐ |
|---|---|
| OS | Ubuntu (Linux 6.17, x86-64) |
| ๋์ค์ด์ ๋ธ๋ฌ | GNU objdump (binutils) |
| ์๋ฎฌ๋ ์ดํฐ | unicorn 2.1.2 (pip install unicorn) |
| GUI ์คํ | wine 10.0 + winetricks vcrun2022 (mfc140u.dll) |
| ํ๋ฉด ์บก์ณ | Xvfb ยท xdotool ยท ImageMagick |
| ๋์ | babycmp.exe md5 845e7763ffb4246b3f294b418b0d9b9c |
reproduce.sh ํ๋๋ก ๋ณต์๊ณผ ๊ต์ฐจ๊ฒ์ฆ์ด ๊ฐ์ด ๋๋ค.
#!/usr/bin/env bash
# babycmp (DreamHack Silver 2, reversing) โ ํ ๋ฐฉ ์ฌํ.
# solve.py ๊ฐ babycmp.exe ์์ ์ง์ ๊ธฐ๋๊ฐยทํคยท์ ๋์ฌ๋ฅผ ์ฝ์ด ์๋ฆฌ์ผ์ ๋ณต์ํ๊ณ ,
# emulate.py ๊ฐ ๋ฐ์ด๋๋ฆฌ์ ๋ณํ ๋ฃจํ๋ฅผ Unicorn ์ผ๋ก ๊ทธ๋๋ก ๋๋ ค ๊ต์ฐจ๊ฒ์ฆํ๋ค.
#
# ํ์: python3, (๊ต์ฐจ๊ฒ์ฆ๊น์ง ํ๋ ค๋ฉด) pip install unicorn
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ .json'))['flag'])" 2>/dev/null || echo '')
OUT=$(timeout 300 python3 solve.py 2>&1) || true
echo "$OUT" | tail -8
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
# ๊ต์ฐจ๊ฒ์ฆ: ๋ฐ์ด๋๋ฆฌ ๊ธฐ๊ณ์ด๋ฅผ ์ค์ ๋ก ์คํํด ๊ฐ์ ๊ฐ์ด ๋์ค๋์ง (unicorn ์์ผ๋ฉด ๊ฑด๋๋)
if [ -n "$FLAG" ] && python3 -c "import unicorn" 2>/dev/null; then
echo
if timeout 300 python3 emulate.py "$FLAG" 2>&1 | tail -1 | grep -q MATCH; then
echo "โ emulate.py: ๋ฐ์ด๋๋ฆฌ ๋ณํ ๋ฃจํ์ ์ผ์น"
else
echo "โ emulate.py: ๋ถ์ผ์น โ ์ฌ๊ตฌํ์ด ํ๋ ธ๋ค"; exit 1
fi
else
echo; echo "(unicorn ๋ฏธ์ค์น โ ์๋ฎฌ๋ ์ด์
๊ต์ฐจ๊ฒ์ฆ ์๋ต)"
fi
if [ -n "$FLAG" ] && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
echo; echo "โ
PASS $FLAG"; exit 0
fi
echo; echo "โ FAIL (์ป์ ๊ฐ: '${FLAG:-์์}' / ๊ธฐ๋: '$EXPECT')"; exit 1์ถ๋ ฅ ๋์ ์ด๋ ๋ค.
FLAG: DH{y0u_4r3_cmp__ma5t3r!}
โ emulate.py: ๋ฐ์ด๋๋ฆฌ ๋ณํ ๋ฃจํ์ ์ผ์น
โ
PASS DH{y0u_4r3_cmp__ma5t3r!}๐ ๊ฒฐ๋ก
์ ๋ต์ ๋ค๊ณ ์๋ ํ๋ก๊ทธ๋จ์ ์ ๋ต์ ๋ด์ค ์๋ฐ์ ์๋ค.
์ด ๋ฌธ์ ์ ๊ฒ์ฆ์ "์ ๋ ฅ์ ๋ณํํด์ ์ ์ฅ๋ ๊ฐ๊ณผ ๊ฐ์์ง ๋ณธ๋ค"๋ ๊ตฌ์กฐ๋ค. ์ ์ฅ๋ ๊ฐ์ด ๋ฐ์ด๋๋ฆฌ ์์ ์๊ณ ๋ณํ์ด ๊ฐ์ญ์ด๋ฉด, ๊ทธ ๋๋ก ์ ๋ต์ ๊ณ์ฐํ ์ ์๋ค. ์๋ฒ์ ๋ฌผ์ด๋ณด๋ ๊ตฌ์กฐ์๋ค๋ฉด 24๊ธ์๋ฅผ ๋ค ๋งํ๊ธฐ ์ ์ ์๋ฌด๊ฒ๋ ๋ชป ์ป์์ ํ ๋ฐ, ํด๋ผ์ด์ธํธ ์์์ ๋๋๋ ๋น๊ต ๋์์ด ๊ณง ๋ต์ด ๋๋ค.
๋ผ์ด์ ์ค ํค๋ ์คํ๋ผ์ธ ์ธ์ฆ์ ์ด๋ ๊ฒ ์ง๋ฉด ์ ๋๋ ์ด์ ๊ฐ ์ฌ๊ธฐ ์๋ค. ๊ฒ์ฆ ๋ก์ง๊ณผ ๊ธฐ๋๊ฐ์ด ๊ฐ์ ์คํํ์ผ์ ๋ค์ด ์์ผ๋ฉด ๋๋ ํ๋ฅผ ์ผ๋ง๋ ์น๋ ์๊ฐ ๋ฌธ์ ๋ค. ์๋ช ๊ฒ์ฆ์ฒ๋ผ ์ ๋ต์ ๋ชฐ๋ผ๋ ํ์ธ๋ง ๋๋ ๊ตฌ์กฐ๋ก ๊ฐ์ผ ํ๋ค.
์๋ฆฌ๋ผ๋ฆฌ ์ฝํ์ง ์์ผ๋ฉด ๋ธ๋ฃจํธํฌ์ค๊ฐ 24๋ฒ์ผ๋ก ๋๋๋ค.
๊ฐ์ด๋ฐ 20๊ธ์๋ฅผ ํต์งธ๋ก ๋งํ๋ ค๋ฉด ๊ฒฝ์ฐ์ ์๊ฐ 95์ 20์ ๊ณฑ์ด์ง๋ง, ์ค์ ๋ก๋ ์๋ฆฌ๋ง๋ค 95๋ฒ์ฉ ์ค๋ฌผ๋ค ๋ฒ ๋๋ฆฌ๋ฉด ๋๋ค. ๊ฐ ์๋ฆฌ์ ๊ณ์ฐ์ ์ ๊ธ์๊ฐ ์ ์์ด๊ธฐ ๋๋ฌธ์ด๋ค.
์ํธ์์ CBC ๊ฐ์ ์ฒด์ด๋์ ์ฐ๋ ์ด์ ๊ฐ ์ด๊ฒ์ด๋ค. ํ ์๋ฆฌ์ ๊ฒฐ๊ณผ๊ฐ ๋ค๋ฅธ ์๋ฆฌ์ ๋ฒ์ง์ง ์์ผ๋ฉด ์ ์ ํ์์ ๋น์ฉ์ด ๊ณฑ์ ์์ ๋ง์ ์ผ๋ก ๋ด๋ ค์๋๋ค.
์์ผ๋ก ์ฎ๊ธด ์ฌ๊ตฌํ์ ๊ธฐ๊ณ๋ก ํ ๋ฒ ๋ ์ฌ ๋ด์ผ ํ๋ค.
์ด์ ๋ธ๋ฆฌ๋ฅผ ์ฝ์ด ํ์ด์ฌ์ผ๋ก ์ฎ๊ธฐ๋ ์๊ฐ "๋ด๊ฐ ์ ๋๋ก ์ฝ์๋"๋ผ๋ ๋ถํ์ค์ฑ์ด ์๊ธด๋ค. ๊ฐ์ด ๋ง์ผ๋ฉด ๋๊ฐ ๋ง์ ๊ฒ์ด์ง๋ง, ์ด๋ฒ์ฒ๋ผ ํ๋ฆฐ ๊ฒ ๋ณํ์์ด ์๋๋ผ ๋ด๊ฐ ๋ง๋ ๊ฐ์ง ๊ฐ์ฒด ๋ ์ด์์์ธ ๊ฒฝ์ฐ๋ ์๋ค. ์ 16๊ฐ๊ฐ ๋ง์๋จ์ด์ ธ์ ํ์ฐธ ์๋ฑํ ๋ฐ๋ฅผ ๋ดค๋ค.
Unicorn ์ผ๋ก ๋ฌธ์ ๋ฐ์ด๋๋ฆฌ์ ๋ช ๋ น์ ๊ทธ๋๋ก ๋๋ฆฌ๋ ๋ฐ ๋ ๊ฑด ์คํฌ๋ฆฝํธ 90์ค ์ ๋๋ค. ๊ทธ ์ ๋๋ฉด "์ฝ์ ๋๋ก ์ฎ๊ฒผ๋ค"๋ฅผ ์ฆ๊ฑฐ๋ก ๋ฐ๊พธ๊ธฐ์ ์ผ ํธ์ด๋ค.
์ถ์ ์์ ํ์ ์ ์๊ฐ๋ณด๋ค ๋ง์ด ๋จ๋๋ค.
PDB ๊ฒฝ๋ก์์ ๋น๋ ๋๋ ํ ๋ฆฌ์ ๊ณ์ ์ด๋ฆ์ด, ์ฆ์น๊ฐ์์ ์ถ์ ์ ๋๋ค์์ด ๊ทธ๋๋ก ๋์๋ค.
ํค๊ฐ neko_hat ์ธ ๊ฑธ ๋ฏธ๋ฆฌ ์์๋ค๊ณ ๋ฌธ์ ๊ฐ ์ฌ์์ง์ง ์์ง๋ง, Release ๋น๋๊ฐ
์ด๋๊น์ง ์ง์ ์ฃผ๋์ง์ ๋ํ ๊ฐ์ ํ์คํ ์๊ธด๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ