๋ฌธ์ : DreamHack โ .so what? ๋ถ๋ฅ: reversing ๋์ด๋: ๐ฅ Gold 3 FLAG:
DH{20654ccdb7c43bd1ab398283f9895ac285e8c419c4c157db2f3f50de92599bd8}
๋ฌธ์ ์ค๋ช ์ ํ ์ค์ด๋ค.
... .so... wait, what???
์์ถ์ ํ๋ฉด ์ ์ ๋ ๊ฒ ์ผ๋์ง ๋ฐ๋ก ์๊ฒ ๋๋ค.
file extracted/main; ls extracted/lib | wc -l; find extracted/lib -name "*.so" -printf "%s\n" | sort -n | uniq -c
.so ํ์ผ์ด 1,257๊ฐ๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | .so what? (id 1347) |
| ๋์ด๋ | ๐ฅ Gold 3 |
| ๋ถ๋ฅ | reversing |
| ์ ๊ณต ํ์ผ | main (PIE, not stripped) + lib/*.so 1,257๊ฐ (20MB) |
| ์ํฌํธ | read ยท printf ยท puts ยท dlopen ยท dlsym ยท exit |
| ํต์ฌ | .so = ์ํ, f_d = ๋ผ๋ฒจ d ์ธ ์ ์ด โ 1,257์ํ ๊ฒฐ์ ์ ์คํ ๋งํ |
| ๋ต | 64์ hex, ๊ฐ์ 19,792๊ฐ ์ค ์ฑ๊ณต ๊ฒฝ๋ก 1๊ฐ |
ํฌ๊ธฐ๊ฐ ์ธ ์ข ๋ฅ๋ฟ์ด๋ผ๋ ๊ฒ ์ฒซ ๋จ์๋ค. 16,200๋ฐ์ดํธ๊ฐ 1,236๊ฐ, 15,560๋ฐ์ดํธ๊ฐ 20๊ฐ, ๊ทธ๋ฆฌ๊ณ 16,192๋ฐ์ดํธ๊ฐ ํ๋. ์์ํ๋ฅผ ๋จผ์ ๋ณด๋ฉด ๋๋ค.
ํ์ด ํ๋ฆ์ ์ด๋ ๊ฒ ํ๋ฌ๊ฐ๋ค. main ์ด dlopen/dlsym ๋ง ์ด๋ค๋ ๊ฑธ ๋ณด๊ณ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ๊ณ ๊ฐ๋ ๊ตฌ์กฐ๋ผ๊ณ ์ง์ํ๋ค. .so ํ๋๋ฅผ ์ด์ด ๋ณด๋ ํจ์๊ฐ f_0~f_f ์ด์ฌ์ฏ ๊ฐ์๊ณ , ๊ฐ๊ฐ ๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ๋ ๊ฒ๊น์ง ํ์ธํ๋ฉด์ ์คํ ๋งํ๋ผ๋ ๊ฒ ํ์ ๋๋ค.
๊ฑฐ๊ธฐ์๋ถํฐ๋ ๋ฆฌ๋ฒ์ฑ์ด ์๋๋ผ ๊ทธ๋ํ ๋ฌธ์ ๋ค. ์ ์ดํ๋ฅผ ๋ฝ๊ณ ๊ฒฝ๋ก๋ฅผ ์ฐพ์ผ๋ฉด ๋๋๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ dlopen ๊ณผ dlsym
๋ ํจ์๋ง ์๋ฉด ์ด ๋ฌธ์ ์ ๊ณจ๊ฒฉ์ด ๋ณด์ธ๋ค.
dlopen(๊ฒฝ๋ก, ํ๋๊ทธ) ๋ ๊ณต์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์คํ ์ค์ ๋ฉ๋ชจ๋ฆฌ๋ก ์ฌ๋ฆฐ๋ค. ์ปดํ์ผํ ๋ ๋งํฌํด ๋๋ ๊ฒ ์๋๋ผ, ํ๋ก๊ทธ๋จ์ด ๋๋ ๋์ค์ ํ์ผ ์ด๋ฆ์ ์ ํด์ ๋ถ๋ฅผ ์ ์๋ค. ํ๋ฌ๊ทธ์ธ ๊ตฌ์กฐ๊ฐ ๋ํ์ ์ธ ์ฉ๋๋ค.
dlsym(ํธ๋ค, ์ด๋ฆ) ์ ๊ทธ๋ ๊ฒ ์ฌ๋ฆฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ด๋ฆ์ผ๋ก ์ฌ๋ณผ์ ์ฐพ์ ์ฃผ์๋ฅผ ๋๋ ค์ค๋ค. ์ด๋ฆ์ด ๋ฌธ์์ด์ด๋, ๊ทธ ๋ฌธ์์ด์ ์คํ ์ค์ ์กฐ๋ฆฝํ ์ ์๋ค.
๋์ ํฉ์น๋ฉด "์ด๋ค ํ์ผ์ ์ด์ง"๋ "์ด๋ค ํจ์๋ฅผ ๋ถ๋ฅผ์ง"๋ ๋ฐํ์์ ์ ํด์ง๋ค. ์ ์ ๋ถ์๊ธฐ๊ฐ ํธ์ถ ๊ทธ๋ํ๋ฅผ ๊ทธ๋ฆด ์ ์๋ค๋ ๋ป์ด๊ธฐ๋ ํ๋ค.
์ด ๋ฌธ์ ๋ ๊ทธ ์ฑ์ง์ ๊ทธ๋๋ก ๋ฌด๊ธฐ๋ก ์ด๋ค. ํ์ผ ์ด๋ฆ๊ณผ ์ฌ๋ณผ ์ด๋ฆ์ ์ ๋ ฅ ๊ธ์๋ก ๊ฒฐ์ ํ๊ฒ ๋ง๋ค์ด, ์ฝ๋๊ฐ ์๋๋ผ ํ์ผ ๋ฐฐ์น ์์ฒด์ ์ ๋ต์ ์จ๊ฒจ ๋๋ค.
๋ฌด์์ด ๋ค์ด ์๋
nm -D extracted/main | grep " U "; strings -a extracted/main | grep -E "lib/|Flag|input|Wrong"
์ํฌํธ์ dlopenยทdlsym, ๋ฌธ์์ด์ lib/start.so ์ Flag is DH{%s}. %s ๋ก ์ฐ๋๋ค๋ ๊ฑด ์ฐ๋ฆฌ ์
๋ ฅ์ด ๊ทธ๋๋ก ํ๋๊ทธ๋ผ๋ ๋ป์ด๋ค.
๐ฌ main ์ฝ๊ธฐ
์ ๋ ฅ์ ๋ฐ๋ ๊ณณ
# annot_main.sed โ ์ฃผ์๋ก ์ค์ ์ง์ ํด ์ฃผ์์ ์น๋๋ค
s|^ 1368:.*|& <-- setvbuf 3์ข
|
s|^ 1394:.*|& <-- 0x41 = 65๋ฐ์ดํธ ์ฝ๋๋ค|
s|^ 13a1:.*|& <-- read(0, buf, 0x41)|
s|^ 13a6:.*|& <-- buf[64] = 0|
s|^ 13b1:.*|& <-- verify(buf) : 64๊ธ์๊ฐ 0-9a-f ์ธ๊ฐ|
s|^ 13b6:.*|& <-- 0x101 = RTLD_LAZY + RTLD_GLOBAL|
s|^ 13c5:.*|& <-- dlopen("lib/start.so")|
s|^ 13ee:.*|& <-- ์ฌ๋ณผ ์ด๋ฆ ๋ง๋ค๊ธฐ : 'f'|
s|^ 13f2:.*|& <-- '_'|
s|^ 13f6:.*|& <-- buf[0] โ "f_<์ฒซ ๊ธ์>"|
s|^ 140f:.*|& <-- dlsym(handle, "f_X")|
s|^ 143c:.*|& <-- ์ธ์๋ buf+1 (ํ ๊ธ์ ์๋น)|
s|^ 1447:.*|& <-- ์ฐพ์ ํจ์๋ฅผ ํธ์ถ|
s|^ 144b:.*|& <-- 0 ์ด๋ฉด Wrong!|
s|^ 1463:.*|& <-- printf("Flag is DH{%s}", buf)|objdump -d -M intel --no-show-raw-insn --start-address=0x1304 --stop-address=0x13ee extracted/main | sed -f annot_main.sed
read(0, buf, 0x41) ๋ก 65๋ฐ์ดํธ๋ฅผ ๋ฐ๊ณ buf[64] = 0. ๊ทธ๋ฆฌ๊ณ verify(buf) ๋ก ๊ฒ์ฌํ ๋ค dlopen("lib/start.so", 0x101).
0x101 ์ RTLD_LAZY | RTLD_GLOBAL ์ด๋ค. RTLD_GLOBAL ์ด ๋ถ์ด ์์ด ๋์ค์ ์ด๋ฆฌ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ์๋ก์ ์ฌ๋ณผ์ ๋ณผ ์ ์๋ค.
์ฌ๋ณผ ์ด๋ฆ์ ๋ง๋ค์ด ๋ถ๋ฅด๋ ๊ณณ
objdump -d -M intel --no-show-raw-insn --start-address=0x13ee --stop-address=0x1494 extracted/main | sed -f annot_main.sed
์ฌ๊ธฐ๊ฐ ํต์ฌ์ด๋ค. ์คํ์ 'f', '_', buf[0], '\0' ์ ์ฐจ๋ก๋ก ๋ฃ์ด "f_<์ฒซ ๊ธ์>" ๋ผ๋ ๋ฌธ์์ด์ ๋ง๋ค๊ณ , ๊ทธ ์ด๋ฆ์ผ๋ก dlsym ํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ฐพ์ ํจ์๋ฅผ buf+1 ๋ก ํธ์ถํ๋ค.
C ๋ก ์ฎ๊ธฐ๋ฉด ์ด๋ ๋ค.
int main(void)
{
char buf[0x41] = {0};
initialize();
puts("Please give me your input!");
printf("> ");
read(0, buf, 0x41);
buf[0x40] = 0;
verify(buf); // 64๊ธ์๊ฐ 0-9a-f ์ธ์ง
void *h = dlopen("lib/start.so", RTLD_LAZY | RTLD_GLOBAL);
if (!h) { puts("Failed to load a library."); exit(1); }
char name[4] = { 'f', '_', buf[0], 0 }; // โ ์ฌ๋ณผ ์ด๋ฆ์ ์
๋ ฅ์ผ๋ก ๋ง๋ ๋ค
int (*fn)(char *) = dlsym(h, name);
if (!fn) { puts("Failed to load a function."); exit(1); }
if (fn(buf + 1)) // โ ํ ๊ธ์ ์๋นํ๊ณ ๋๊น
printf("Flag is DH{%s}\n", buf);
else
puts("Wrong!");
}์
๋ ฅ์ด ๊ณง ํ๋๊ทธ์ด๋ฏ๋ก, ์ฐ๋ฆฌ๊ฐ ํ ์ผ์ fn ์ด 0 ์๋ ๊ฐ์ ๋๋ ค์ฃผ๊ฒ ๋ง๋๋ 64์๋ฅผ ์ฐพ๋ ๊ฒ์ด๋ค.
์ ๋ ฅ ์ํ๋ฒณ
# annot_verify.sed
s|^ 1299:.*|& <-- c <= '/' ์ด๋ฉด hex ๋ฌธ์ ๊ฒ์ฌ๋ก|
s|^ 12ad:.*|& <-- c <= '9' ์ด๋ฉด ํต๊ณผ (0-9)|
s|^ 12c1:.*|& <-- c <= '`' ์ด๋ฉด ์คํจ|
s|^ 12d5:.*|& <-- c <= 'f' ์ด๋ฉด ํต๊ณผ (a-f)|
s|^ 12e3:.*|& <-- ์๋๋ฉด "Wrong input." ํ exit|
s|^ 12fa:.*|& <-- i <= 0x3f : ์ ํํ 64๊ธ์|objdump -d -M intel --no-show-raw-insn --start-address=0x1270 --stop-address=0x1304 extracted/main | sed -f annot_verify.sed
0x30~0x39(0-9)์ 0x61~0x66(a-f), ๊ทธ๋ฆฌ๊ณ i <= 0x3f ๋ผ ์ ํํ 64๊ธ์.
64์ ์๋ฌธ์ hex โ ํ์ ๊ณต๊ฐ์ 16โถโด ์ด๋ค. ๋ฌด์ฐจ๋ณ ๋์ ์ ์ ์ด์ ๋ ผ์ธ๊ณ , ๊ตฌ์กฐ๋ฅผ ๋ด์ผ ํ๋ค.
๐ฃ ํต์ฌ โ .so ๊ฐ ์ํ, f_d ๊ฐ ์ ์ด
๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ๋๋ฅผ ์ด์ด ๋ณธ๋ค
nm -D --defined-only extracted/lib/start.so; echo ---; strings -a extracted/lib/start.so | grep -c "^lib/"
๋ด๋ณด๋ด๋ ํจ์๊ฐ f_0 ~ f_f ์ ํํ 16๊ฐ. ๊ทธ๋ฆฌ๊ณ lib/... ๊ฒฝ๋ก ๋ฌธ์์ด๋ 16๊ฐ.
hex ์ซ์ ํ๋๋น ํจ์ ํ๋, ํจ์ ํ๋๋น ๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ๋. ์ฌ๊ธฐ์ ๋ชจ์์ด ์กํ๋ค.
f_d ์์ ๋ณธ๋ค
# annot_fd.sed
s|^ 11b8:.*|& <-- 0x101 = RTLD_LAZY + RTLD_GLOBAL|
s|^ 11c7:.*|& <-- dlopen("lib/<์ด ๊ธ์์ ์ ํด์ง ๋ค์>.so")|
s|^ 11f0:.*|& <-- 'f'|
s|^ 11f4:.*|& <-- '_'|
s|^ 11fc:.*|& <-- p[0] = ๋ค์ ๊ธ์|
s|^ 1214:.*|& <-- dlsym(handle, "f_<๋ค์ ๊ธ์>")|
s|^ 1241:.*|& <-- ์ธ์๋ p+1|
s|^ 124c:.*|& <-- ์ฌ๊ท ํธ์ถ. ๋ฐํ๊ฐ์ ๊ทธ๋๋ก ๋๊ธด๋ค|objdump -d -M intel --no-show-raw-insn extracted/lib/start.so | sed -n "/<f_0>:/,/<f_1>:/p" | sed -f annot_fd.sed
main ๊ณผ ๋๊ฐ์ ์ง์ ํ๋ค.
int f_0(char *p)
{
void *h = dlopen("lib/6fb3f5f2b4.so", RTLD_LAZY | RTLD_GLOBAL); // โ ์ด ๊ธ์์ ๊ณ ์ ๋ ๋ค์ ํ์ผ
char name[4] = { 'f', '_', p[0], 0 };
int (*fn)(char *) = dlsym(h, name);
return fn(p + 1); // ๋ฐํ๊ฐ์ ๊ทธ๋๋ก ์ฌ๋ ค๋ณด๋ธ๋ค
}์ ๋ฆฌํ๋ฉด ์ด๋ ๋ค.
dlopen ๋์์ "์ง๊ธ ํจ์์ ์ด๋ฆ"์ด ๊ฒฐ์ ํ๋ค. f_0 ์์์๋ ํญ์ ๊ฐ์ ํ์ผ์ ์ฐ๋ค. ์ฆ ์ง์ ์ ์๋นํ ๊ธ์๊ฐ ๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ณ ๋ฅธ๋ค.
dlsym ์ด๋ฆ์ "๋ค์ ๊ธ์"๊ฐ ๊ฒฐ์ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ธ์๋ฅผ ํ ์นธ ๋ฐ์ด ์ฌ๊ทํ๋ค.
๋ฐํ๊ฐ์ ๊ทธ๋๋ก ์๋ก ์ ๋ฌ๋๋ค. ์ค๊ฐ์์ ๊ฐ์ ๋ฐ๊พธ์ง ์๋๋ค.
์ด๊ฒ ์ ํํ ๊ฒฐ์ ์ ์ ํ ์คํ ๋งํ๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ = ์ํ, f_d = ๋ผ๋ฒจ d ์ธ ์ ์ด, ์
๋ ฅ ๋ฌธ์์ด = ์ ์ด ์ด.

๋์ ์ด๋์ธ๊ฐ
์ ์ด ์ํ๋ dlopen ์ ํ์ง๋ง, ์ด๋๊ฐ์๋ ๊ทธ๋ฅ ๋ต์ ๋๋ ค์ฃผ๋ ์ํ๊ฐ ์์ด์ผ ํ๋ค. ํฌ๊ธฐ๊ฐ ์์ 20๊ฐ(15,560๋ฐ์ดํธ)๊ฐ ๊ทธ ํ๋ณด๋ค. ํฌ๊ธฐ์ ๊ธฐ๋์ง ๋ง๊ณ ์ฝ๋๋ก ํ๋ณํ๋ค โ dlopen@plt ๋ฅผ ์ ๋ถ๋ฅด๋ .so ๋ฅผ ๊ณ ๋ฅด๋ฉด ๋๋ค.
#!/usr/bin/env python3
"""dlopen ์ ํ์ง ์๋ .so(= ์ข
๋ฃ ์ํ)๋ฅผ ์ฐพ์, 16๊ฐ ํจ์๊ฐ ๋ญ ๋๋ ค์ฃผ๋์ง ๋ณธ๋ค."""
import pathlib
import re
import subprocess
from concurrent.futures import ProcessPoolExecutor
HERE = pathlib.Path(__file__).resolve().parent
LIB = HERE / "extracted" / "lib"
DIGITS = "0123456789abcdef"
FUNC_RE = re.compile(r"^[0-9a-f]+ <f_([0-9a-f])>:$")
RET_RE = re.compile(r"mov\s+eax,0x([0-9a-f]+)\b")
def scan(path_str: str):
path = pathlib.Path(path_str)
out = subprocess.run(["objdump", "-d", "-M", "intel", "--no-show-raw-insn", str(path)],
capture_output=True, text=True, check=True).stdout
if "<dlopen@plt>" in out:
return None # ์ ์ด ์ํ
rets, cur = {}, None
for line in out.splitlines():
m = FUNC_RE.match(line.strip())
if m:
cur = m.group(1)
continue
if cur is None:
continue
m = RET_RE.search(line)
if m and cur not in rets:
rets[cur] = int(m.group(1), 16)
return path.name, rets
def main():
libs = sorted(str(p) for p in LIB.glob("*.so"))
with ProcessPoolExecutor() as ex:
found = [r for r in ex.map(scan, libs, chunksize=16) if r]
print(f"[*] ์ ์ฒด {len(libs)}๊ฐ ์ค dlopen ์๋ .so = {len(found)}๊ฐ (์ข
๋ฃ ์ํ)")
total_one = 0
for name, rets in sorted(found):
ones = [d for d in DIGITS if rets.get(d) == 1]
total_one += len(ones)
if ones:
print(f" โ
{name} โ f_{','.join(ones)} ๊ฐ 1 ์ ๋๋ ค์ค๋ค")
print(f"[+] ์ข
๋ฃ ์ํ์ ํจ์ {len(found) * 16}๊ฐ ์ค 1 ์ ๋๋ ค์ฃผ๋ ๊ฒ : {total_one}๊ฐ")
print(" ๋๋จธ์ง๋ ์ ๋ถ 0 โ ์ฆ ์ฑ๊ณต์ผ๋ก ๊ฐ๋ ๊ธธ์ ํ๋๋ฟ์ด๋ค")
if __name__ == "__main__":
main()python3 scan_terminals.py
์ข
๋ฃ ์ํ 20๊ฐ ร 16ํจ์ = 320๊ฐ ์ค 1 ์ ๋๋ ค์ฃผ๋ ๊ฑด ๋ฑ ํ๋, 219f2e3164.so ์ f_8.
์๋ฝ ์ํ์ ๋ง์ง๋ง ๊ธ์๊ฐ ๋์์ ํ์ ๋๋ค.
๐ ์ฝ์ง
โถ๐ณ๏ธ ์ฝ์ง 1 โ ํ์ผ ํฌ๊ธฐ๋ก ์ข ๋ฅ๋ฅผ ๊ฐ๋๋ค๊ฐ ๊ทผ๊ฑฐ๋ฅผ ๋ฐ๊ฟจ๋ค
ํฌ๊ธฐ๊ฐ ์ธ ์ข ๋ฅ๋ผ๋ ๊ฑธ ๋ณด๊ณ ์ฒ์์ ๊ทธ๊ฑธ ๊ทธ๋๋ก ํ๋ณ ๊ธฐ์ค์ผ๋ก ์ผ๋ค.
for f in $(ls -l lib/*.so | awk '{if ($5==15560) print $NF}'); do ... done๋ต์ ๋ง์๋ค. 15,560๋ฐ์ดํธ 20๊ฐ๊ฐ ์ ๋ง๋ก ์ข ๋ฃ ์ํ์๋ค. ๊ทธ๋ฐ๋ฐ ๊ทผ๊ฑฐ๊ฐ ์ฝํ๋ค.
ํฌ๊ธฐ๊ฐ ๋ค๋ฅธ ์ด์ ๋ "dlopen ์ ์ ํด์ ์ฝ๋์ ๋ฌธ์์ด์ด ์ค์๋ค" ์ธ๋ฐ, ๊ทธ๊ฑด ๊ฒฐ๊ณผ์ง ์ ์๊ฐ ์๋๋ค. ๋ค๋ฅธ ๋ฐฐํฌ๋ณธ์ด๋ ๋ค๋ฅธ ์ปดํ์ผ ์ต์
์ด๋ฉด ์ซ์๊ฐ ๋ฌ๋ผ์ง๊ณ , ํํ ํฌ๊ธฐ๊ฐ ๊ฐ์ ์ ์ด ์ํ๊ฐ ํ๋๋ผ๋ ์์ผ๋ฉด ์กฐ์ฉํ ํ๋ฆฐ๋ค.
๊ทธ๋์ scan_terminals.py ๋ ํฌ๊ธฐ๋ฅผ ์์ ์ ๋ณธ๋ค. objdump ์ถ๋ ฅ์ <dlopen@plt> ๊ฐ ์๋๊ฐ๋ก ๊ฐ๋ฅธ๋ค.
if "<dlopen@plt>" in out:
return None # ์ ์ด ์ํ๊ฒฐ๊ณผ๋ ๊ฐ์์ง๋ง(20๊ฐ), ์ด์ ๋ "์ 20๊ฐ์ธ์ง"๊ฐ ์ค๋ช ๋๋ค. ๋ถ๋ฅ ๊ธฐ์ค์ ๊ด์ฐฐ๋ ์๊ด๊ด๊ณ๊ฐ ์๋๋ผ ์ ์๋ฅผ ์จ์ผ ํ๋ค โ ์ฌ๊ธฐ์๋ ๊ทธ ์ฐจ์ด๊ฐ ๊ฒฐ๊ณผ๋ฅผ ์ ๋ฐ๊ฟจ์ ๋ฟ์ด๋ค.
โถ๐ณ๏ธ ์ฝ์ง 2 โ sed ์ฃผ์ ํ ์ค์ด ์กฐ์ฉํ ์ฌ๋ผ์ก๋ค
๋์ค์ด์
๋ธ์ <-- ์ฃผ์์ ์น๋ ๊ท์น์ 16๊ฐ ์ผ๋๋ฐ, ๋ถ์ ๊ฑด 15๊ฐ์๋ค.
์๋ฌ๋ ์ ๋๊ณ ๊ฒฝ๊ณ ๋ ์๋ค. sed ๋ ๋งค์นญ์ด ์ ๋๋ฉด ๊ทธ๋ฅ ๋์ด๊ฐ๋ค. ๊ฐ์๋ฅผ ์ธ์ง ์์์ผ๋ฉด ๋ชป ์์์ฑ์ ๊ฒ์ด๋ค.
objdump ... | sed -f annot_main.sed | grep -c '<--'๋น ์ง ๊ฑด ์ด ์ค์ด์๋ค.
s|^ 13b6:.*|& <-- RTLD_LAZY \| RTLD_GLOBAL|s|...|...| ์์ ๊ตฌ๋ถ์๊ฐ | ์ธ๋ฐ ์นํ๋ฌธ ์์ | ๋ฅผ ๊ทธ๋๋ก ์ผ๋ค. sed ๋ ๊ฑฐ๊ธฐ์ ์นํ๋ฌธ์ด ๋๋ ์ค ์๊ณ ๋ค๋ฅผ ์ต์
์ผ๋ก ์ฝ๋ค๊ฐ ๊ท์น์ ๋ฒ๋ฆฐ๋ค.
dlopen ํ๋๊ทธ๋ฅผ ์ค๋ช
ํ๋ ค๋ RTLD_LAZY | RTLD_GLOBAL ์ด๋ผ๊ณ ์ฐ๋ ๊ฒ ์์ฐ์ค๋ฌ์์ ์๊ธด ์ผ์ด๋ค. ๋ฌธ๊ตฌ๋ฅผ RTLD_LAZY + RTLD_GLOBAL ๋ก ๋ฐ๊ฟ ํด๊ฒฐํ๋ค(๊ตฌ๋ถ์๋ฅผ # ๋ฑ์ผ๋ก ๋ฐ๊ฟ๋ ๋๋ค).
์ฃผ์์ ์น์์ผ๋ฉด grep -c '<--' ๋ก ๊ฐ์๋ฅผ ํ์ธํ๋ค. ์ ๊ทธ๋ฌ๋ฉด ๊ธ์ ์ค๋ฆฌ๋ ์บก์ณ์์ ์ค๋ช
ํ ์ค์ด ์กฐ์ฉํ ๋น ์ง๋ค.
๐ฏ ์ ์ดํ๋ฅผ ๋ฝ๋๋ค
f_d ์์ dlopen ์ธ์ ๋ฌธ์์ด์ ์ฝ์ผ๋ฉด ์ ์ด ๋์์ด ๋์จ๋ค. ๋์ค์ด์
๋ธ์์ call ...<dlopen@plt> ๋ฐ๋ก ์์ lea rax,[rip+...] ๊ฐ ๊ฐ๋ฆฌํค๋ ์ฃผ์์ C ๋ฌธ์์ด์ด ๊ทธ๊ฒ์ด๋ค.
#!/usr/bin/env python3
"""1257๊ฐ .so ์์ ์ ์ดํ๋ฅผ ๋ฝ์ DFA ๋ก ๋ง๋ ๋ค."""
import json
import pathlib
import re
import subprocess
from concurrent.futures import ProcessPoolExecutor
from elftools.elf.elffile import ELFFile
HERE = pathlib.Path(__file__).resolve().parent
LIB = HERE / "extracted" / "lib"
FUNC_RE = re.compile(r"^[0-9a-f]+ <f_([0-9a-f])>:$")
LEA_RE = re.compile(r"lea\s+rax,\[rip\+0x[0-9a-f]+\]\s+#\s+([0-9a-f]+)")
RET0_RE = re.compile(r"mov\s+eax,0x([01])\b")
def read_cstring(elf_path: pathlib.Path, vaddr: int) -> str:
"""๊ฐ์์ฃผ์ vaddr ์ ์๋ NUL ์ข
๋ฃ ๋ฌธ์์ด์ ํ์ผ์์ ์ฝ๋๋ค."""
with open(elf_path, "rb") as fh:
elf = ELFFile(fh)
for seg in elf.iter_segments():
if seg["p_type"] != "PT_LOAD":
continue
lo, hi = seg["p_vaddr"], seg["p_vaddr"] + seg["p_filesz"]
if lo <= vaddr < hi:
fh.seek(seg["p_offset"] + (vaddr - lo))
raw = fh.read(256)
return raw.split(b"\x00", 1)[0].decode()
raise ValueError(f"{elf_path.name}: vaddr {vaddr:#x} ๋ฅผ ๋ชป ์ฐพ์")
def parse_one(path_str: str):
path = pathlib.Path(path_str)
out = subprocess.run(
["objdump", "-d", "-M", "intel", "--no-show-raw-insn", str(path)],
capture_output=True, text=True, check=True).stdout
edges, rets, cur, pending_lea = {}, {}, None, None
for line in out.splitlines():
line = line.strip()
m = FUNC_RE.match(line)
if m:
cur, pending_lea = m.group(1), None
continue
if cur is None:
continue
m = LEA_RE.search(line)
if m:
pending_lea = int(m.group(1), 16)
continue
if "<dlopen@plt>" in line and pending_lea is not None:
s = read_cstring(path, pending_lea)
if s.startswith("lib/"):
edges[cur] = s[len("lib/"):]
pending_lea = None
continue
m = RET0_RE.search(line)
if m and cur not in edges:
rets[cur] = int(m.group(1))
return path.name, edges, rets
def main():
libs = sorted(str(p) for p in LIB.glob("*.so"))
print(f"[*] .so {len(libs)}๊ฐ ํ์ฑ")
dfa, terminal = {}, {}
with ProcessPoolExecutor() as ex:
for name, edges, rets in ex.map(parse_one, libs, chunksize=16):
if edges:
dfa[name] = edges
else:
terminal[name] = rets
(HERE / "dfa.json").write_text(json.dumps(dfa, indent=0, sort_keys=True))
(HERE / "terminal.json").write_text(json.dumps(terminal, indent=0, sort_keys=True))
print(f"[+] ์ ์ด ์ํ {len(dfa)}๊ฐ / ์ข
๋ฃ ์ํ {len(terminal)}๊ฐ")
bad = [k for k, v in dfa.items() if len(v) != 16]
print(f"[*] ์ ์ด 16๊ฐ๊ฐ ์๋ ์ํ: {len(bad)} {bad[:3]}")
accept = {k: [d for d, v in r.items() if v == 1] for k, r in terminal.items()}
accept = {k: v for k, v in accept.items() if v}
print(f"[+] 1 ์ ๋๋ ค์ฃผ๋ ์ข
๋ฃ ์ํ: {accept}")
if __name__ == "__main__":
main()python3 extract_dfa.py
์ ์ด ์ํ 1,237๊ฐ ร 16 = ๊ฐ์ 19,792๊ฐ. ์ ์ด๊ฐ 16๊ฐ๊ฐ ์๋ ์ํ๋ ํ๋๋ ์๋ค โ ํ์ฑ์ด ๋น ์ง์์ด ๋๋ค๋ ๋ป์ด๋ค.
๐ Full Exploit
verify ๊ฐ ์ ํํ 64๊ธ์๋ฅผ ์๊ตฌํ๋ฏ๋ก ๊ฒฝ๋ก ๊ธธ์ด๊ฐ ์ ํํ 63์ด์ด์ผ ํ๋ค. ๋ง์ง๋ง 64๋ฒ์งธ ๊ธ์๋ ์ข
๋ฃ ์ํ์ ํจ์๋ฅผ ๊ณ ๋ฅผ ๋ฟ ์ ์ด๋ฅผ ํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ฌ๊ธฐ์ "์ต๋จ ๊ฒฝ๋ก๋ฅผ ์ฐพ์ผ๋ฉด ๋์ง ์๋" ์ถ์๋ฐ, ์ ์ด ๊ทธ๋ํ์๋ ์ฌ์ดํด์ด ์์ด์ ์ต๋จ ๊ฒฝ๋ก๊ฐ 63๋ณด๋ค ์งง๊ฒ ๋์ฌ ์ ์๋ค. ๊ทธ๋ฌ๋ฉด verify ์์ ๊ฑธ๋ฆฐ๋ค. ๊ทธ๋์ ๊ธธ์ด๋ณ ๋๋ฌ ๊ฐ๋ฅ ์งํฉ์ ๋ค์์๋ถํฐ ๊ณ์ฐํ๋ค. reach[k] ๋ฅผ "k๋ฒ ๋ ์ ์ดํ๋ฉด ์๋ฝ ์ํ์ ๋ฟ์ ์ ์๋ ์ํ๋ค"๋ก ๋๊ณ , ์์ผ๋ก ๊ฑธ์ ๋ reach[k-1] ์์ ์๋ ์ ์ด๋ง ๊ณ ๋ฅด๋ฉด ๊ธธ์ด๊ฐ ์ ํํ ๋ง๋๋ค.
์ค์ ๋ก ์ด ๊ทธ๋ํ๊ฐ ์ด๋ ๋์ง๋ ์ธ์ด ๋ณด๋ฉด ๋์จ๋ค.
# path_stats.py โ ์๋ฝ ์ํ๊น์ง ๊ฐ๋ ๊ฒฝ๋ก๊ฐ ๊ธธ์ด๋ณ๋ก ๋ช ๊ฐ์ธ์ง ์ผ๋ค
cnt, per_len = {goal: 1}, {}
for k in range(1, MAXLEN + 1):
nxt = {}
for src, e in dfa.items():
t = sum(cnt.get(e[d], 0) for d in DIGITS)
if t:
nxt[src] = t
cnt = nxt
if cnt.get(START):
per_len[k] = cnt[START]python3 path_stats.py
BFS ์ต๋จ์ด ๋ง์นจ 63์ด์๊ณ , ๊ธธ์ด 80 ์ดํ์์ ์๋ฝ ์ํ๋ก ๊ฐ๋ ๊ฒฝ๋ก๋ 63์ง๋ฆฌ ํ๋๋ฟ์ด๋ค. ์ฑ๊ณต ๊ฒฝ๋ก ์์๋ ์ฌ์ดํด๋ ๊ฐ๋ฆผ๊ธธ๋ ์๋ค๋ ๋ป์ด๋ผ, ์ด ๋ฌธ์ ์ ํํด์๋ ์ต๋จ ๊ฒฝ๋ก๋ก ํ์ด๋ ๊ฐ์ ๋ต์ด ๋์์ ๊ฒ์ด๋ค.
๊ทธ๋๋ ๊ธธ์ด๋ฅผ ๊ณ ์ ํด ์ฐพ๋ ์ชฝ์ ํํ๋ค. ๊ทธ๋์ผ "์ด ์ข๊ฒ ์ต๋จ์ด 63์ด์๋ค"์ ๊ธฐ๋์ง ์๋๋ค.
#!/usr/bin/env python3
""".so what? (DreamHack, Gold 3 / reversing) โ solver
๊ตฌ์กฐ:
main ์ด 64์ hex ๋ฅผ ๋ฐ์ verify() ๋ก 0-9a-f ์ธ์ง ํ์ธํ ๋ค
dlopen("lib/start.so") โ dlsym("f_" + s[0]) โ ๊ทธ ํจ์๋ฅผ s+1 ๋ก ํธ์ถ
ํ๋ค. ๊ฐ .so ์ f_d ๋ ๋ค์
dlopen("lib/<์ ํด์ง ๋ค์>.so") โ dlsym("f_" + p[0]) โ f(p+1)
์ด๋ฏ๋ก **.so = ์ํ, f_d = ๋ผ๋ฒจ d ์ธ ์ ์ด**๋ฅผ ๊ฐ๋ ๊ฒฐ์ ์ ์คํ ๋งํ๋ค.
์ข
๋ฃ ์ํ(dlopen ์์ด ์์๋ง ๋๋ ค์ฃผ๋ .so) 20๊ฐ ์ค
`219f2e3164.so` ์ `f_8` ๋ง 1 ์ ๋๋ ค์ค๋ค. ๋๋จธ์ง๋ ์ ๋ถ 0.
๋ฐ๋ผ์ ์ ๋ต์
start.so ์์ ์ถ๋ฐํด 63๋ฒ ์ ์ดํด 219f2e3164.so ์ ๋ฟ๊ณ
๋ง์ง๋ง 64๋ฒ์งธ ๊ธ์๊ฐ 8
์ธ 64์ hex ๋ฌธ์์ด์ด๋ค. flag = DH{๊ทธ ๋ฌธ์์ด}
โ
์ฃผ์: ์ ์ด ๊ทธ๋ํ์ ์ฌ์ดํด์ด ์์ด "์ต๋จ ๊ฒฝ๋ก"๊ฐ ์๋๋ผ
"๊ธธ์ด๊ฐ ์ ํํ 63" ์ธ ๊ฒฝ๋ก๋ฅผ ์ฐพ์์ผ ํ๋ค. ๊ธธ์ด๋ณ ๋๋ฌ ๊ฐ๋ฅ ์งํฉ์
๋ค์์๋ถํฐ ๊ณ์ฐ(์ญ๋ฐฉํฅ BFS)ํด ๊ทธ ์์์๋ง ์์ผ๋ก ๊ฑธ์ด๊ฐ๋ฉด ๋๋ค.
"""
import argparse
import json
import pathlib
import sys
HERE = pathlib.Path(__file__).resolve().parent
DIGITS = "0123456789abcdef"
START = "start.so"
LENGTH = 64 # verify() ๊ฐ ๊ฒ์ฌํ๋ ๊ธ์ ์
def load():
dfa = json.loads((HERE / "dfa.json").read_text())
terminal = json.loads((HERE / "terminal.json").read_text())
return dfa, terminal
def accepting(terminal: dict) -> tuple[str, str]:
"""1 ์ ๋๋ ค์ฃผ๋ (์ข
๋ฃ์ํ, ๋ง์ง๋ง๊ธ์) ๋ฅผ ์ฐพ๋๋ค."""
hits = [(lib, d) for lib, rets in terminal.items()
for d, v in rets.items() if v == 1]
if len(hits) != 1:
sys.exit(f"์๋ฝ ์ํ๊ฐ {len(hits)}๊ฐ๋ค: {hits}")
return hits[0]
def solve(dfa: dict, goal: str, steps: int) -> str | None:
"""start ์์ goal ๊น์ง ์ ํํ steps ๋ฒ ์ ์ดํ๋ ๊ฒฝ๋ก์ ๋ผ๋ฒจ ๋ฌธ์์ด."""
# reach[k] = k ๋ฒ ๋ ์ ์ดํ๋ฉด goal ์ ๋ฟ์ ์ ์๋ ์ํ ์งํฉ
reach = [set() for _ in range(steps + 1)]
reach[0] = {goal}
back: dict[str, list[str]] = {}
for src, edges in dfa.items():
for d in DIGITS:
back.setdefault(edges[d], []).append(src)
for k in range(1, steps + 1):
nxt = set()
for st in reach[k - 1]:
nxt.update(back.get(st, ()))
reach[k] = nxt
if START not in reach[steps]:
return None
out, cur = [], START
for k in range(steps, 0, -1):
for d in DIGITS: # ์ฌ์ ์์ผ๋ก ์ฒซ ๋ฒ์งธ๋ฅผ ๊ณ ๋ฅธ๋ค
if dfa[cur][d] in reach[k - 1]:
out.append(d)
cur = dfa[cur][d]
break
else:
sys.exit("๊ฒฝ๋ก ์ฌ๊ตฌ์ฑ ์คํจ")
return "".join(out)
def count_paths(dfa: dict, goal: str, steps: int) -> int:
"""๊ธธ์ด๊ฐ ์ ํํ steps ์ธ ๊ฒฝ๋ก๊ฐ ๋ช ๊ฐ์ธ์ง ์ผ๋ค (์ ์ผ์ฑ ํ์ธ)."""
cnt = {goal: 1}
for _ in range(steps):
nxt: dict[str, int] = {}
for src, edges in dfa.items():
t = sum(cnt.get(edges[d], 0) for d in DIGITS)
if t:
nxt[src] = t
cnt = nxt
return cnt.get(START, 0)
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--count", action="store_true", help="๊ฒฝ๋ก ๊ฐ์๋ ์ผ๋ค")
a = ap.parse_args()
dfa, terminal = load()
goal, last = accepting(terminal)
print(f"[*] ์ํ {len(dfa)}๊ฐ(์ ์ด) + {len(terminal)}๊ฐ(์ข
๋ฃ)")
print(f"[+] ์๋ฝ ์ํ : {goal} ์ f_{last}")
prefix = solve(dfa, goal, LENGTH - 1)
if prefix is None:
print("[-] ๊ธธ์ด๊ฐ ๋ง๋ ๊ฒฝ๋ก๊ฐ ์๋ค")
return 1
flag_body = prefix + last
print(f"[+] ๊ฒฝ๋ก ๊ธธ์ด : {len(flag_body)}์")
if a.count:
n = count_paths(dfa, goal, LENGTH - 1)
print(f"[*] ๊ธธ์ด {LENGTH - 1} ๊ฒฝ๋ก ์ : {n}๊ฐ")
print(f"[+] FLAG: DH{{{flag_body}}}")
return 0
if __name__ == "__main__":
sys.exit(main())python3 solve.py --count
--count ๋ ๊ธธ์ด 63 ๊ฒฝ๋ก๊ฐ ๋ช ๊ฐ์ธ์ง ๋์ ๊ณํ๋ฒ์ผ๋ก ์ธ๋ ์ต์
์ด๋ค. ๊ฒฐ๊ณผ๋ 1๊ฐ โ ๋ต์ด ์ ์ผํ๋ค.
์ ์ ์ ์ดํ๊ฐ ๋ฐํ์๊ณผ ๊ฐ์๊ฐ
objdump ๋ก ์ฝ์ ํ๊ฐ ์ค์ ์คํ๊ณผ ์ด๊ธ๋๋ฉด ๋ต๋ ํ๋ฆฐ๋ค. strace ๋ก main ์ด ์ฌ๋ .so ์์๋ฅผ ๋ฐ์ ์์ธก๊ณผ ํ ์นธ์ฉ ๋์กฐํ๋ค.
#!/usr/bin/env python3
"""์ ์ ์ผ๋ก ๋ฝ์ ์ ์ดํ๊ฐ ์ค์ ์คํ๊ณผ ๋ง๋์ง ๋์กฐํ๋ค."""
import json
import pathlib
import re
import subprocess
import sys
HERE = pathlib.Path(__file__).resolve().parent
ANSWER = "20654ccdb7c43bd1ab398283f9895ac285e8c419c4c157db2f3f50de92599bd8"
OPEN_RE = re.compile(r'"lib/([0-9a-z]+\.so)"')
def traced_order() -> list[str]:
p = subprocess.run(
["strace", "-f", "-e", "trace=openat", "./main"],
input=ANSWER + "\n", cwd=HERE / "extracted",
capture_output=True, text=True, timeout=120)
return OPEN_RE.findall(p.stderr)
def predicted_order() -> list[str]:
dfa = json.loads((HERE / "dfa.json").read_text())
order, cur = ["start.so"], "start.so"
for d in ANSWER[:-1]: # ๋ง์ง๋ง ๊ธ์๋ ์ข
๋ฃ ์ํ์ ํจ์๋ฅผ ๊ณ ๋ฅผ ๋ฟ dlopen ์ ์ ํ๋ค
cur = dfa[cur][d]
order.append(cur)
return order
def main() -> int:
got, want = traced_order(), predicted_order()
print(f"[*] ์
๋ ฅ : {ANSWER[:16]}... (64์)")
print(f"[*] strace ๊ฐ ์ฐ .so : {len(got)}๊ฐ")
print(f"[*] ์ ์ดํ ์์ธก : {len(want)}๊ฐ")
print()
for i in (0, 1, 2, 31, 61, 62, 63):
if i < min(len(got), len(want)):
mark = "์ผ์น" if got[i] == want[i] else "โ
๋ถ์ผ์น"
print(f" [{i:2d}] ์คํ={got[i]:<16} ์์ธก={want[i]:<16} {mark}")
print()
if got == want:
print("[+] 64๊ฐ๊ฐ ํ ์นธ๋ ์ด๊ธ๋์ง ์๋๋ค โ ์ ์ ์ ์ดํ๊ฐ ๋ฐํ์๊ณผ ๊ฐ๋ค")
return 0
bad = next(i for i, (a, b) in enumerate(zip(got, want)) if a != b)
print(f"[-] {bad}๋ฒ์งธ์์ ๊ฐ๋ฆฐ๋ค: ์คํ={got[bad]} / ์์ธก={want[bad]}")
return 1
if __name__ == "__main__":
sys.exit(main())python3 trace_check.py
start.so ํ๋ + ์ ์ด 63๋ฒ = 64๊ฐ. ์์๊ฐ ํ ์นธ๋ ์ด๊ธ๋์ง ์๋๋ค.
์ค์ ๋ฐ์ด๋๋ฆฌ๋ก
#!/usr/bin/env python3
"""์ฐพ์ ๋ฌธ์์ด์ ์ค์ main ์ ๋จน์ฌ Flag is DH{...} ๊ฐ ๋์ค๋์ง ํ์ธํ๋ค."""
import subprocess
ANSWER = "20654ccdb7c43bd1ab398283f9895ac285e8c419c4c157db2f3f50de92599bd8"
CWD = "extracted"
def run(s: str) -> str:
p = subprocess.run(["./main"], input=s + "\n", cwd=CWD,
capture_output=True, text=True, timeout=30)
return p.stdout.strip().splitlines()[-1] if p.stdout.strip() else "(์ถ๋ ฅ ์์)"
cases = [
("์ ๋ต", ANSWER),
("๋ง์ง๋ง ๊ธ์๋ง ๋ฐ๊ฟ", ANSWER[:-1] + "9"),
("์ฒซ ๊ธ์๋ง ๋ฐ๊ฟ", "3" + ANSWER[1:]),
("๊ฐ์ด๋ฐ ํ ๊ธ์ ๋ฐ๊ฟ", ANSWER[:32] + ("0" if ANSWER[32] != "0" else "1") + ANSWER[33:]),
]
for label, s in cases:
print(f"{label:20s} len={len(s):3d} โ {run(s)}")python3 verify.py
DH{20654ccdb7c43bd1ab398283f9895ac285e8c419c4c157db2f3f50de92599bd8}ํ ๊ธ์๋ง ๋ฐ๊ฟ๋ ์ ๋ถ Wrong!. ์ด๋ ์์น๋ฅผ ๊ฑด๋๋ฆฌ๋ ๊ทธ ์ง์ ์์ ๊ฒฝ๋ก๊ฐ ๊ฐ๋ผ์ ธ ๋ค๋ฅธ ์ข
๋ฃ ์ํ๋ก ๋จ์ด์ง๋ค.
๐งช ์ฌํ
./reproduce.sh์คํ๋ผ์ธ ๋ฌธ์ ๋ผ ์ธ์๊ฐ ์๋ค. ํ์ํ ๊ฑด python3(+pyelftools), objdump, strace ์
์ด๋ค.
reproduce.sh ๊ฐ ํ๋ ์ผ์ ์ด ๊ธ์ ์์์ ๊ฐ๋ค. ์ข
๋ฃ ์ํ๋ฅผ ์ฐพ๊ณ โ ์ ์ดํ๋ฅผ ๋ฝ๊ณ โ ๊ธธ์ด 63 ๊ฒฝ๋ก๋ฅผ ์ธ์ด ์ ์ผํ์ง ํ์ธํ๊ณ โ strace ๋ก ๋ฐํ์๊ณผ ๋์กฐํ๊ณ โ ์ค์ main ์ผ๋ก ํ๋๊ทธ๋ฅผ ๋ฐ๋๋ค.
ํ์ ์ ์ด๋ ๊ฒ ์ฝ๋๋ค.
| ํ๋ฉด | ๋ป | ๋์ฒ |
|---|---|---|
์ ์ด 16๊ฐ๊ฐ ์๋ ์ํ: N (N>0) | lea/dlopen ์ง์ง๊ธฐ๊ฐ ์คํจ | objdump ๋ฒ์ ํ์ธ |
์๋ฝ ์ํ๊ฐ 0๊ฐ๋ค | ์ข ๋ฃ ์ํ ํ๋ณ์ด ์ด๊ธ๋จ | ๋ค๋ฅธ ๋ฐฐํฌ๋ณธ์ธ์ง ํ์ธ |
๊ธธ์ด๊ฐ ๋ง๋ ๊ฒฝ๋ก๊ฐ ์๋ค | ๊ธธ์ด ์์๊ฐ ํ๋ฆผ | verify ์ 0x3f ๋ฅผ ๋ค์ ํ์ธ |
๊ฒฝ๋ก ์ : 2๊ฐ ์ด์ | ๋ต์ด ์ฌ๋ฟ โ ํด์ ์ค๋ฅ ๊ฐ๋ฅ | ์ ์ดํ๋ฅผ trace_check ๋ก ๋์กฐ |
strace ๋์กฐ์์ ๋ถ์ผ์น | ์ ์ ํ๊ฐ ํ๋ฆผ | ๊ทธ ์ธ๋ฑ์ค์ .so ๋ฅผ ์ง์ ๋์ค์ด์
๋ธ |
Wrong! | ์คํ ๊ถํ ์์ | chmod +x extracted/main |
extract_dfa.py ๋ .so 1,257๊ฐ๋ฅผ objdump ๋ก ์ ๋ถ ํ์ฑํ์ง๋ง ํ๋ก์ธ์ค ํ์ ์จ์ 7์ด๋ฉด ๋๋๋ค. ํ์ผ์ด ๋ง๋ค๊ณ ๊ฒ๋จน์ ๊ท๋ชจ๋ ์๋๋ค.
๐ ๊ฒฐ๋ก
๋ฐํ์์ ์ด๋ฆ์ผ๋ก ๋ถ๋ฅด๋ฉด ์ ์ ํธ์ถ ๊ทธ๋ํ๊ฐ ์ฌ๋ผ์ง๋ค.
dlopen ์ ์ธ์๋ dlsym ์ ์ฌ๋ณผ ์ด๋ฆ๋ ์คํ ์ค์ ๋ง๋ค์ด์ง๋, ๋์ค์ด์
๋ธ๋ฌ๊ฐ "์ด ํจ์๊ฐ ์ ํจ์๋ฅผ ๋ถ๋ฅธ๋ค"๋ฅผ ๊ทธ๋ ค ์ค ์ ์๋ค. ์ด ๋ฌธ์ ๋ ๊ทธ ์ฑ์ง์ ์ด์ฉํด ๋ก์ง์ด ์๋๋ผ ํ์ผ ๋ฐฐ์น์ ์ ๋ต์ ์จ๊ฒผ๋ค. main ๊ณผ .so ํ๋๋ง ์ฝ์ผ๋ฉด ์ฝ๋๋ 30์ค๋ ์ ๋๋๋ฐ, ๋ต์ 20MB ์ด์น ํ์ผ ์ด๋ฆ ์์ ์๋ค.
๊ตฌ์กฐ๊ฐ ๋ณด์ด๋ฉด ๋ฆฌ๋ฒ์ฑ์ด ์๋๋ผ ๊ทธ๋ํ ๋ฌธ์ ๊ฐ ๋๋ค.
.so = ์ํ, f_d = ์ ์ด๋ผ๋ ๊ฑธ ์์์ฐจ๋ฆฐ ๋ค๋ก๋ ๋์ค์ด์
๋ธ์ด ํ์ ์๋ค. ํ์ํ ๊ฑด ์ ์ดํ ํ๋๊ณ , ๊ทธ๊ฑด ๊ธฐ๊ณ์ ์ผ๋ก ๋ฝํ๋ค. 1,257๊ฐ๋ฅผ ํ๋์ฉ ์ฝ์ ์ผ์ด ์๋๋ผ 1,257๊ฐ์์ ๊ฐ์ ๊ฐ์ ๋ฝ์๋ด๋ ์คํฌ๋ฆฝํธ๋ฅผ ์ธ ์ผ์ด๋ค.
"์ต๋จ"์ด ์๋๋ผ "์ ํํ ๊ทธ ๊ธธ์ด"๋ฅผ ์ฐพ์์ผ ํ๋ค.
์ฌ์ดํด์ด ์๋ ๊ทธ๋ํ์์ ๊ธธ์ด๋ฅผ ๋ง์ถ๋ ๊ฑด ์ต๋จ๊ฒฝ๋ก์ ๋ค๋ฅธ ๋ฌธ์ ๋ค. ๋ค์์๋ถํฐ reach[k] ๋ฅผ ์ฑ์ ๋๊ณ ์์ผ๋ก ๊ฑธ์ผ๋ฉด ๊ฐ๋จํ ํด๊ฒฐ๋๋๋ฐ, BFS ๋ฅผ ๊ทธ๋๋ก ์ฐ๋ฉด ์งง์ ๋ต์ด ๋์ verify ์์ ๋งํ๋ค. ์ ์ฝ์ด "๋๋ฌ"์ด ์๋๋ผ "๋๋ฌ + ๊ธธ์ด"์ผ ๋ ๋ ๋์ค๋ ํจ์ ์ด๋ค.
์ ์ ์ผ๋ก ์ฝ์ ๋ชจ๋ธ์ ๋ฐํ์๊ณผ ๋์กฐํด ๋๋ฉด ๋ง์์ด ํธํ๋ค.
strace -e trace=openat ํ ์ค๋ก ์ค์ dlopen ์์๋ฅผ ๋ฐ์ ์์ธก๊ณผ ๋ง์ถฐ ๋ดค๋ค. 64์นธ์ด ์ ๋ถ ์ผ์นํ๋ ์ ์ดํ๊ฐ ๋ง๋ค๋ ๊ฒ ํ์ธ๋๊ณ , ๋ง์ฝ ์ด๊ธ๋ฌ๋ค๋ฉด ์ด๋ ์ธ๋ฑ์ค์์ ๊ฐ๋ฆฌ๋์ง๊น์ง ๋ฐ๋ก ๋์์ ๊ฒ์ด๋ค. ํ์ฑ์ผ๋ก ๋ง๋ ๋ชจ๋ธ์ ์ด๋ ๊ฒ ํ ๋ฒ ๋์กฐํด ๋๋ ๊ฐ์ด ํฌ๋ค.
๋ฐฉ์ด ์ชฝ์์ ๋ณด๋ฉด, ์ด๋ฐ ๊ตฌ์กฐ๋ ๋ถ์์ ๊ท์ฐฎ๊ฒ ๋ง๋ค ๋ฟ ๋ง์ง๋ ๋ชปํ๋ค. ํ์ผ 1,257๊ฐ๊ฐ ์์์ ์ผ๋ก ๋ณด์ฌ๋ ๊ท์น์ด ํ๋๋ก ํต์ผ๋ผ ์์ผ๋ฉด ์คํฌ๋ฆฝํธ ํ ํธ์ ๋ฌด๋์ง๋ค. ์คํ๋ ค ๊ท์น์ด ๊ท ์ผํ๋ค๋ ์ โ ๋ชจ๋ .so ๊ฐ ์ ํํ 16๊ฐ ํจ์๋ฅผ ๊ฐ๊ณ ์ ๋ถ ๊ฐ์ ๋ชจ์์ด๋ผ๋ ์ โ ์ด ์๋ ์ถ์ถ์ ์ฝ๊ฒ ๋ง๋ค์ด ์คฌ๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ