[๐Ÿฅ‡ Gold 4] 5๋ฐ”์ดํŠธ ์Šคํ… 97๊ฐœ๋กœ ๋ถ€์ˆœ ๊ฑด ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ์˜€๋‹ค โ€” DreamHack Vernichtet ํ’€์ด

2026-08-17ยท1๋ถ„ ์ฝ๊ธฐยท

[๐Ÿฅ‡ Gold 4] 5๋ฐ”์ดํŠธ ์Šคํ… 97๊ฐœ๋กœ ๋ถ€์ˆœ ๊ฑด ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ์˜€๋‹ค โ€” DreamHack Vernichtet ํ’€์ด

.text ์ „์ฒด์— `eb ff c1 ff c9` ๊ฐ€ 97๊ฐœ ๋ฐ•ํ˜€ ์žˆ๋‹ค. jmp ๊ฐ€ ์ž๊ธฐ ์˜คํผ๋žœ๋“œ๋กœ ๋›ฐ์–ด๋“œ๋Š” ๊ฒน์นจ ๋ช…๋ น์ด๋ผ ์„ ํ˜• ์Šค์œ• ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ๋Š” ์‹คํ–‰๋˜์ง€๋„ ์•Š๋Š” ์œ ๋ น ๋ช…๋ น์„ ๋ณด์—ฌ ์ค€๋‹ค. ๊ธธ์ด๋ฅผ ์œ ์ง€ํ•œ ์ฑ„ NOP ์œผ๋กœ ๋ฎ์–ด ์ฝ”๋“œ๋ฅผ ๋˜์‚ด๋ฆฌ๊ณ , ๊ทธ ์•ˆ์— ์ˆจ์–ด ์žˆ๋˜ 15ร—15 Hidato ํผ์ฆ์„ ํ’€์–ด ๋‹ต์•ˆ ํŒŒ์ผ์˜ sha256 ์„ ํ”Œ๋ž˜๊ทธ๋กœ ๋ฐ›์•„๋ƒˆ๋‹ค.

๋ฌธ์ œ: DreamHack โ€” Vernichtet ๋ถ„๋ฅ˜: reversing ๋‚œ์ด๋„: ๐Ÿฅ‡ Gold 4 FLAG: DH{e309147b588c517bb4100064d6185e5430ebad23d83e601327c4907bb0232292}

๋ฌธ์ œ ์„ค๋ช…์€ ๋”ฑ ํ•œ ์ค„์ด๋‹ค. "I destroyed the binary, but it still works :>"

๋…์ผ์–ด ์ œ๋ชฉ Vernichtet ๋„ ๊ฐ™์€ ๋ง์ด๋‹ค โ€” ํŒŒ๊ดด๋๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ญ๊ฐ€ ํŒŒ๊ดด๋๋Š”์ง€๋Š” ์•ˆ ์•Œ๋ ค ์ค€๋‹ค.


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…Vernichtet
๋‚œ์ด๋„๐Ÿฅ‡ Gold 4
๋ถ„๋ฅ˜reversing
์ œ๊ณต ํŒŒ์ผmain (ELF64 PIE, stripped, 15,160 ๋ฐ”์ดํŠธ)
์„œ๋ฒ„์—†์Œ โ€” ์˜คํ”„๋ผ์ธ ๋ฌธ์ œ
ํ•ต์‹ฌ ๊ธฐ๋ฒ•๊ฒน์นจ ๋ช…๋ น(overlapping instruction)์„ ์ด์šฉํ•œ ์•ˆํ‹ฐ ๋””์Šค์–ด์…ˆ๋ธ”๋ฆฌ + 15ร—15 Hidato ํผ์ฆ

ํ’€์ด ํ๋ฆ„์€ ์ด๋ ‡๋‹ค. ํŒŒ๊ดด๋œ ์ •์ฒด๊ฐ€ ์•ˆํ‹ฐ ๋””์Šค์–ด์…ˆ๋ธ”๋ฆฌ ์Šคํ…์ด๋ผ๋Š” ๊ฑธ ํ™•์ธํ•˜๊ณ  โ†’ ๊ธธ์ด๋ฅผ ์œ ์ง€ํ•œ ์ฑ„ NOP ์œผ๋กœ ๋ฎ์–ด ์ฝ”๋“œ๋ฅผ ๋˜์‚ด๋ฆฐ ๋‹ค์Œ โ†’ ๋˜์‚ด์•„๋‚œ ๋‘ ๊ฒ€์‚ฌ ํ•จ์ˆ˜๋ฅผ ์ฝ์–ด ์ •๋‹ต ํŒŒ์ผ์˜ ์กฐ๊ฑด์„ ์•Œ์•„๋‚ด๊ณ  โ†’ ๊ทธ ์กฐ๊ฑด์ด ์‚ฌ์‹ค ํผ์ฆ์ด๋ผ๋Š” ๊ฑธ ๋ˆˆ์น˜์ฑ„๊ณ  ํ’€๋ฉด, ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์Šค์Šค๋กœ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฑ‰๋Š”๋‹ค.



๐Ÿ”ฌ ์ •์ฐฐ โ€” ๋ฌธ์ž์—ด์ด ์ ˆ๋ฐ˜์„ ๋งํ•ด ์ค€๋‹ค

๋ฐ›์€ ๊ฑด ํŒŒ์ผ ํ•˜๋‚˜๋ฟ์ด๋‹ค. ๋Š˜ ํ•˜๋˜ ๊ฒƒ๋ถ€ํ„ฐ ํ›‘๋Š”๋‹ค.

#!/usr/bin/env bash
# cap_recon.sh โ€” ๋ฐฐํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ ์ •์ฐฐ: ํฌ๋งท / ๋ณดํ˜ธ๊ธฐ๋ฒ• / ๋ˆˆ์— ๋„๋Š” ๋ฌธ์ž์—ด
file extracted/main
readelf -h extracted/main | grep -E 'Type:|Entry point|Number of section headers'
readelf -d extracted/main | grep -E 'NEEDED|BIND_NOW|PIE'
strings -a extracted/main | grep -E 'Usage|Correct|Wrong answer|Fread|sha256sum'
./cap_recon.sh

file ๊ณผ readelf -h/-d ๋กœ ๋ณธ Vernichtet ๋ฐฐํฌ๋ณธ โ€” PIEยทstripped ELF64 ์ด๊ณ  ์„น์…˜ ํ—ค๋” 29๊ฐœ๊ฐ€ ๋ฉ€์ฉกํžˆ ๋‚จ์•„ ์žˆ๋‹ค. strings ์— sha256sum ์„ ๋Œ๋ ค ํ”Œ๋ž˜๊ทธ๋ฅผ ๋งŒ๋“œ๋Š” bash ํ•œ ์ค„์ด ๊ทธ๋Œ€๋กœ ๋ณด์ธ๋‹ค
file ๊ณผ readelf -h/-d ๋กœ ๋ณธ Vernichtet ๋ฐฐํฌ๋ณธ โ€” PIEยทstripped ELF64 ์ด๊ณ  ์„น์…˜ ํ—ค๋” 29๊ฐœ๊ฐ€ ๋ฉ€์ฉกํžˆ ๋‚จ์•„ ์žˆ๋‹ค. strings ์— sha256sum ์„ ๋Œ๋ ค ํ”Œ๋ž˜๊ทธ๋ฅผ ๋งŒ๋“œ๋Š” bash ํ•œ ์ค„์ด ๊ทธ๋Œ€๋กœ ๋ณด์ธ๋‹ค

๋งˆ์ง€๋ง‰ ๋ฌธ์ž์—ด์ด ์ด ๋ฌธ์ œ์˜ ๊ทœ์น™์„ ํ†ต์งธ๋กœ ์•Œ๋ ค ์ค€๋‹ค.

bash -c "echo DH{$(sha256sum '%s' | awk '{print $1}')}"

ํ”Œ๋ž˜๊ทธ๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ ์•ˆ์— ์—†๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค์–ด ๋‚ธ ์ •๋‹ต ํŒŒ์ผ์˜ sha256 ์ด ๊ณง ํ”Œ๋ž˜๊ทธ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ์ •๋‹ต ํŒŒ์ผ์˜ ๋ฐ”์ดํŠธ๋ฅผ ํ•˜๋‚˜๋„ ํ‹€๋ฆฌ์ง€ ์•Š๊ฒŒ ๋ณต์›ํ•ด์•ผ ํ•œ๋‹ค. "๋Œ€์ถฉ ๋งž๋Š” ๋‹ต"์ด๋ผ๋Š” ๊ฑด ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค.

Usage ./main <answer file> ๋กœ ์ธ์ž ํ˜•ํƒœ๋ฅผ ์•Œ ์ˆ˜ ์žˆ๊ณ , Wrong answer. / Fread failed / Correct! ๋กœ ๋ถ„๊ธฐ ๊ตฌ์กฐ๋„ ๋Œ€๋žต ๊ทธ๋ ค์ง„๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 1 โ€” 'ํŒŒ๊ดด๋๋‹ค'๋ฅผ ์„น์…˜ ํ—ค๋” ์–˜๊ธฐ๋กœ ์ฝ์—ˆ๋‹ค

"destroyed the binary but it still works" ๋ฅผ ๋ณด๊ณ  ์ œ์ผ ๋จผ์ € ๋– ์˜ฌ๋ฆฐ ๊ฑด ์„น์…˜ ํ—ค๋” ์‚ญ์ œ์˜€๋‹ค. ํ”„๋กœ๊ทธ๋žจ ํ—ค๋”๋งŒ ์žˆ์œผ๋ฉด ์‹คํ–‰์€ ๋˜๊ณ , ์„น์…˜ ํ—ค๋”๊ฐ€ ์—†์œผ๋ฉด objdump ๊ฐ€ ์‹ฌ๋ณผ๋„ ์„น์…˜๋„ ๋ชป ์žก์œผ๋‹ˆ ๋”ฑ ๋งž๋Š” ์ด์•ผ๊ธฐ ์•„๋‹Œ๊ฐ€.

๊ทธ๋ž˜์„œ readelf -S ๋ถ€ํ„ฐ ์ณค๋Š”๋ฐ, 29๊ฐœ๊ฐ€ ์ „๋ถ€ ๋ฉ€์ฉกํ–ˆ๋‹ค.

  [16] .text             PROGBITS         0000000000001180  00001180
       000000000000074c  0000000000000000  AX       0     0     16
  [18] .rodata           PROGBITS         0000000000002000  00002000
       0000000000000091  0000000000000000   A       0     0     8
  [25] .data             PROGBITS         0000000000004000  00003000
       00000000000002c3  0000000000000000  WA       0     0     32

.text ๋„ .rodata ๋„ .data ๋„ ์ฃผ์†Œยทํฌ๊ธฐ๊ฐ€ ์ •์ƒ์ด๊ณ , readelf -l ์˜ LOAD ์„ธ๊ทธ๋จผํŠธ๋„ ์–ด๊ธ‹๋‚œ ๋ฐ๊ฐ€ ์—†๋‹ค. ELF ๊ตฌ์กฐ๋Š” ์†๋Œ„ ํ”์ ์ด ์—†์—ˆ๋‹ค. ํŒŒ๊ดด๋œ ๊ฑด ๋” ์•ˆ์ชฝ์ด์—ˆ๋‹ค.


๐Ÿ’ฃ ํŒŒ๊ดด์˜ ์ •์ฒด โ€” eb ff c1 ff c9

.text ๋ฅผ ํ†ต์งธ๋กœ ๋œฏ์–ด๋ณด๋ฉด ์ •์ฒด๊ฐ€ ๋ฐ”๋กœ ๋“œ๋Ÿฌ๋‚œ๋‹ค.

objdump -d -M intel --start-address=0x1269 --stop-address=0x12b7 extracted/main

objdump ๋กœ ๋ณธ ์›๋ณธ 0x1269 ํ•จ์ˆ˜ โ€” ์‹ค์ œ ๋ช…๋ น ๋‘์„ธ ๊ฐœ๋งˆ๋‹ค jmp ๊ฐ€ ์ž๊ธฐ ๋ฐ”๋กœ ๋‹ค์Œ ๋ฐ”์ดํŠธ๋กœ ๋›ฐ๊ณ  sar edi,0xc9 ๋ผ๋Š” ์ •์ฒด๋ถˆ๋ช… ๋ช…๋ น์ด ๋ผ์–ด๋“œ๋Š” ํŒจํ„ด์ด ๋ฐ˜๋ณต๋œ๋‹ค
objdump ๋กœ ๋ณธ ์›๋ณธ 0x1269 ํ•จ์ˆ˜ โ€” ์‹ค์ œ ๋ช…๋ น ๋‘์„ธ ๊ฐœ๋งˆ๋‹ค jmp ๊ฐ€ ์ž๊ธฐ ๋ฐ”๋กœ ๋‹ค์Œ ๋ฐ”์ดํŠธ๋กœ ๋›ฐ๊ณ  sar edi,0xc9 ๋ผ๋Š” ์ •์ฒด๋ถˆ๋ช… ๋ช…๋ น์ด ๋ผ์–ด๋“œ๋Š” ํŒจํ„ด์ด ๋ฐ˜๋ณต๋œ๋‹ค

jmp ๊ฐ€ ์ž๊ธฐ ์ž์‹ ์˜ ๋‹ค์Œ ๋ฐ”์ดํŠธ๋กœ ๋›ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋’ค์—” sar edi,0xc9 ๋ผ๋Š”, ์ด ํ”„๋กœ๊ทธ๋žจ ์–ด๋””์—๋„ ์“ธ๋ชจ๊ฐ€ ์—†๋Š” ๋ช…๋ น์ด ๋”ฐ๋ผ๋ถ™๋Š”๋‹ค. ๋‘์„ธ ๋ช…๋ น๋งˆ๋‹ค ํ•œ ๋ฒˆ์”ฉ, ๋๋„ ์—†์ด.

๋ฐ”์ดํŠธ๋กœ ๋ณด๋ฉด ๋‹ค์„ฏ ๊ฐœ๋‹ค. eb ff c1 ff c9.

x86 ์€ ๊ฐ€๋ณ€ ๊ธธ์ด ๋ช…๋ น์–ด๋ผ ์–ด๋””์„œ๋ถ€ํ„ฐ ์ฝ๊ธฐ ์‹œ์ž‘ํ•˜๋А๋ƒ์— ๋”ฐ๋ผ ๊ฐ™์€ ๋ฐ”์ดํŠธ๊ฐ€ ์™„์ „ํžˆ ๋‹ค๋ฅธ ๋ช…๋ น์ด ๋œ๋‹ค. eb ff ๋Š” jmp $+1 โ€” ์ฆ‰ jmp ๋ช…๋ น ์ž์‹ ์˜ ์˜คํผ๋žœ๋“œ ๋ฐ”์ดํŠธ(0xff)๋กœ ์ ํ”„ํ•œ๋‹ค. ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ๋Š” eb ff ๋ฅผ ํ•œ ๋ช…๋ น์œผ๋กœ ์†Œ๋น„ํ•˜๊ณ  ๋‹ค์Œ ๋ฐ”์ดํŠธ์ธ 0xc1 ๋ถ€ํ„ฐ ์ฝ์ง€๋งŒ, CPU ๋Š” ์ ํ”„๋ฅผ ๋”ฐ๋ผ 0xff ๋ถ€ํ„ฐ ์ฝ๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ๋‘ ํ•ด์„์ด ๊ฐˆ๋ฆฐ๋‹ค.

Vernichtet ์˜ 5๋ฐ”์ดํŠธ ์Šคํ… ๋ฐ”์ดํŠธ ๋‹จ์œ„ ๊ทธ๋ฆผ โ€” objdump ๋Š” jmp ์™€ sar edi,0xc9 ๋กœ ์ฝ๊ณ  CPU ๋Š” jmp ๋ฅผ ๋”ฐ๋ผ inc ecx, dec ecx ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค
Vernichtet ์˜ 5๋ฐ”์ดํŠธ ์Šคํ… ๋ฐ”์ดํŠธ ๋‹จ์œ„ ๊ทธ๋ฆผ โ€” objdump ๋Š” jmp ์™€ sar edi,0xc9 ๋กœ ์ฝ๊ณ  CPU ๋Š” jmp ๋ฅผ ๋”ฐ๋ผ inc ecx, dec ecx ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค

๋ง๋กœ๋งŒ ํ•˜๋ฉด ๋ชป ๋ฏฟ์„ ์–˜๊ธฐ๋ผ ๊ฐ™์€ 5๋ฐ”์ดํŠธ๋ฅผ ๋‘ ๋ฒˆ ๋””์Šค์–ด์…ˆ๋ธ”ํ•ด ๋ดค๋‹ค. objdump ์˜ --start-address ๋ฅผ ํ•œ ๋ฐ”์ดํŠธ๋งŒ ์˜ฎ๊ธฐ๋ฉด ๋œ๋‹ค.

#!/usr/bin/env bash
# cap_decode.sh โ€” ๊ฐ™์€ 5๋ฐ”์ดํŠธ๋ฅผ ๋‘ ๊ฐ€์ง€๋กœ ๋””์ฝ”๋”ฉํ•ด ๋ณธ๋‹ค.
#   objdump ๋Š” 0x1271 ๋ถ€ํ„ฐ ์„ ํ˜•์œผ๋กœ ์ฝ๊ณ , CPU ๋Š” jmp ๋ฅผ ๋”ฐ๋ผ 0x1272 ๋ถ€ํ„ฐ ์ฝ๋Š”๋‹ค.
echo '# ์›๋ณธ ๋ฐ”์ดํŠธ (ํŒŒ์ผ ์˜คํ”„์…‹ = ๊ฐ€์ƒ์ฃผ์†Œ, .text ๋Š” 1:1 ๋งคํ•‘)'
xxd -s 0x1271 -l 5 extracted/main
echo
echo '# objdump ์„ ํ˜• ์Šค์œ•์ด ๋ณธ ๊ฒƒ (0x1271 ๋ถ€ํ„ฐ)'
objdump -d -M intel --start-address=0x1271 --stop-address=0x1276 extracted/main | tail -3
echo
echo '# CPU ๊ฐ€ ์‹ค์ œ๋กœ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ (jmp ๋ชฉ์ ์ง€ 0x1272 ๋ถ€ํ„ฐ)'
objdump -d -M intel --start-address=0x1272 --stop-address=0x1276 extracted/main | tail -3
./cap_decode.sh

xxd ์™€ objdump ๋กœ ๊ฐ™์€ 5๋ฐ”์ดํŠธ๋ฅผ ๋‘ ์‹œ์ž‘์ ์—์„œ ๋””์ฝ”๋”ฉํ•œ ๊ฒฐ๊ณผ โ€” 0x1271 ๋ถ€ํ„ฐ ์ฝ์œผ๋ฉด jmp ์™€ sar edi,0xc9 ์ง€๋งŒ 0x1272 ๋ถ€ํ„ฐ ์ฝ์œผ๋ฉด inc ecx ์™€ dec ecx ๊ฐ€ ๋‚˜์˜จ๋‹ค
xxd ์™€ objdump ๋กœ ๊ฐ™์€ 5๋ฐ”์ดํŠธ๋ฅผ ๋‘ ์‹œ์ž‘์ ์—์„œ ๋””์ฝ”๋”ฉํ•œ ๊ฒฐ๊ณผ โ€” 0x1271 ๋ถ€ํ„ฐ ์ฝ์œผ๋ฉด jmp ์™€ sar edi,0xc9 ์ง€๋งŒ 0x1272 ๋ถ€ํ„ฐ ์ฝ์œผ๋ฉด inc ecx ์™€ dec ecx ๊ฐ€ ๋‚˜์˜จ๋‹ค

0x1272 ๋ถ€ํ„ฐ ์ฝ์œผ๋ฉด ff c1 = inc ecx, ff c9 = dec ecx ๋‹ค. ํ•˜๋‚˜ ์˜ฌ๋ฆฌ๊ณ  ํ•˜๋‚˜ ๋‚ด๋ฆฌ๋‹ˆ ๊ฒฐ๊ณผ๋Š” ์•„๋ฌด ์ผ๋„ ์•ˆ ํ•œ ๊ฒƒ๊ณผ ๊ฐ™๋‹ค. ์‹คํ–‰ ์˜๋ฏธ๋กœ๋Š” ์™„๋ฒฝํ•œ no-op ์ด๊ณ , ํ™”๋ฉด์—๋Š” ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ช…๋ น์ด ์ฐํžŒ๋‹ค.

gdb ๋กœ ํ•œ ๋ฒˆ ๋” โ€” ์ •๋ง ๊ทธ ๊ธธ๋กœ ๊ฐ€๋Š”๊ฐ€

์ •์  ํ•ด์„์€ ์ •์  ํ•ด์„์ผ ๋ฟ์ด๋‹ค. ์‹ค์ œ๋กœ CPU ๊ฐ€ 0x1272 ๋ฅผ ๋ฐŸ๋Š”์ง€ ๋Ÿฐํƒ€์ž„์œผ๋กœ ํ™•์ธํ–ˆ๋‹ค. PIE ๋ผ ๋กœ๋“œ ๋ฒ ์ด์Šค๋ฅผ ๊ด€๋ก€๋กœ ๊ฐ€์ •ํ•˜์ง€ ์•Š๊ณ  info proc mappings ์—์„œ ์ง์ ‘ ์ฝ๋Š”๋‹ค.

set pagination off
set confirm off
set disassembly-flavor intel
starti
python
import gdb
 
base = None
for line in gdb.execute("info proc mappings", to_string=True).splitlines():
    f = line.split()
    if len(f) >= 5 and f[-1].endswith("/main"):
        base = int(f[0], 16)
        break
print("PIE base (info proc mappings) = %#x" % base)
 
stub = base + 0x1271
print("\n[objdump ๊ฐ€ ๋ณธ ๊ฒƒ] 0x1271 ๋ถ€ํ„ฐ ์„ ํ˜• ํ•ด์„")
gdb.execute("x/2i %#x" % stub)
 
gdb.execute("break *%#x" % stub)
gdb.execute("continue")
 
print("\n[CPU ๊ฐ€ ์‹ค์ œ๋กœ ๋ฐŸ๋Š” ๊ฒƒ] stepi ๋กœ ๋”ฐ๋ผ๊ฐ„๋‹ค")
for _ in range(3):
    pc = int(gdb.parse_and_eval("$pc"))
    ins = gdb.execute("x/i $pc", to_string=True).strip().replace("=> ", "")
    rcx = int(gdb.parse_and_eval("$rcx")) & 0xFFFFFFFF
    print("  +%#06x  ecx=%#010x  %s" % (pc - base, rcx, ins.split(":", 1)[1].strip()))
    gdb.execute("stepi", to_string=True)
pc = int(gdb.parse_and_eval("$pc"))
ins = gdb.execute("x/i $pc", to_string=True).strip().replace("=> ", "")
print("  +%#06x  %s   <- ์Šคํ…์„ ๋น ์ ธ๋‚˜์˜จ ์ฒซ ์ง„์งœ ๋ช…๋ น" % (pc - base, ins.split(":", 1)[1].strip()))
end
kill
quit
gdb -q -batch -x stub.gdb --args ./extracted/main answer.bin

gdb -batch ๋กœ ์Šคํ…์„ stepi ์ถ”์ ํ•œ ๊ฒฐ๊ณผ โ€” rip ๊ฐ€ 0x1271 ์—์„œ 0x1272, 0x1274, 0x1276 ์ˆœ์œผ๋กœ ์ด๋™ํ•˜๊ณ  ecx ๊ฐ€ 0x555593f0 ์—์„œ f1 ๋กœ ์˜ฌ๋ž๋‹ค ๋‹ค์‹œ f0 ์œผ๋กœ ๋Œ์•„์˜จ๋‹ค
gdb -batch ๋กœ ์Šคํ…์„ stepi ์ถ”์ ํ•œ ๊ฒฐ๊ณผ โ€” rip ๊ฐ€ 0x1271 ์—์„œ 0x1272, 0x1274, 0x1276 ์ˆœ์œผ๋กœ ์ด๋™ํ•˜๊ณ  ecx ๊ฐ€ 0x555593f0 ์—์„œ f1 ๋กœ ์˜ฌ๋ž๋‹ค ๋‹ค์‹œ f0 ์œผ๋กœ ๋Œ์•„์˜จ๋‹ค

rip ๊ฐ€ +0x1271 โ†’ +0x1272 โ†’ +0x1274 โ†’ +0x1276 ์œผ๋กœ ๊ฐ„๋‹ค. 0x1273 ์€ ๋ฐŸํžˆ์ง€๋„ ์•Š๋Š”๋‹ค. ecx ๊ฐ€ ...f0 โ†’ ...f1 โ†’ ...f0 ์œผ๋กœ ์˜ค๋ฅด๋‚ด๋ฆฌ๋Š” ๊ฒƒ๊นŒ์ง€ ๋ณด์ด๋‹ˆ, sar edi,0xc9 ๋Š” ํ™•์‹คํžˆ ์œ ๋ น์ด๋‹ค.

์ด๋Ÿฐ ์Šคํ…์ด .text ์•ˆ์— 97๊ฐœ, 485๋ฐ”์ดํŠธ ์žˆ๋‹ค. .text ํฌ๊ธฐ๊ฐ€ 1,868๋ฐ”์ดํŠธ๋‹ˆ ์ฝ”๋“œ์˜ 26%๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์„ ๋ชฉ์ ์œผ๋กœ ๋“ค์–ด๊ฐ„ ์…ˆ์ด๋‹ค.


๐Ÿงฐ ๋„๊ตฌ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ์†๋Š”๋‹ค โ€” objdump vs radare2

์—ฌ๊ธฐ๊ฐ€ ์žฌ๋ฏธ์žˆ๋Š” ์ง€์ ์ด๋‹ค. ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ์˜ ์ „๋žต์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค๋ฅด๊ฒŒ ๋ง๊ฐ€์ง„๋‹ค.

objdump ๋Š” ์„ ํ˜• ์Šค์œ•์ด๋ผ ์•ž์—์„œ๋ถ€ํ„ฐ ์ˆœ์„œ๋Œ€๋กœ ๊ธธ์ด๋ฅผ ์žฌ๋ฉฐ ์ฝ๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์œ ๋ น ๋ช…๋ น์„ ๋ณด์—ฌ ์ฃผ์ง€๋งŒ, ์Šคํ…์ด ์ •ํ™•ํžˆ 5๋ฐ”์ดํŠธ์— ๋”ฑ ๋งž์ถฐ ๋๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ ๋‹ค์Œ ์ง„์งœ ๋ช…๋ น๋ถ€ํ„ฐ๋Š” ๋‹ค์‹œ ๋™๊ธฐํ™”๋œ๋‹ค. ์ฆ‰ ๋กœ์ง ์ž์ฒด๋Š” ์ฝํžŒ๋‹ค โ€” ๋…ธ์ด์ฆˆ๊ฐ€ 26% ์„ž์˜€์„ ๋ฟ์ด๋‹ค.

radare2 ๋Š” ์žฌ๊ท€ ํ•˜๊ฐ•์ด๋ผ ๋ถ„๊ธฐ๋ฅผ ๋”ฐ๋ผ๊ฐ„๋‹ค. ๊ทธ๋ž˜์„œ ์Šคํ…์„ ์ œ๋Œ€๋กœ ํ•ด์„ํ•˜์ง€๋งŒ, ๋Œ€์‹  5๋ฐ”์ดํŠธ๋งˆ๋‹ค ๊ธฐ๋ณธ ๋ธ”๋ก์ด ๊ฐˆ๋ผ์ ธ ์ œ์–ด ํ๋ฆ„ ๊ทธ๋ž˜ํ”„๊ฐ€ ์ž˜๊ฒŒ ๋ถ€์„œ์ง„๋‹ค.

#!/usr/bin/env bash
# cap_r2.sh โ€” radare2 ๋Š” jmp ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋Š” ์žฌ๊ท€ ํ•˜๊ฐ•์ด๋ผ ์Šคํ…์„ "๋งž๊ฒŒ" ์ฝ๋Š”๋‹ค.
# ๋Œ€์‹  5๋ฐ”์ดํŠธ๋งˆ๋‹ค ๊ธฐ๋ณธ๋ธ”๋ก์ด ๊ฐˆ๋ผ์ ธ CFG ๊ฐ€ ์ž˜๊ฒŒ ๋ถ€์„œ์ง„๋‹ค. ๊ทธ ์ฐจ์ด๋ฅผ ์ˆ˜์น˜๋กœ ๋ณธ๋‹ค.
echo '### ์›๋ณธ (์Šคํ… ์‚ด์•„ ์žˆ์Œ) โ€” 0x1269 ํ•จ์ˆ˜'
r2 -qq -e scr.color=0 -e log.level=0 -c 'aaa; s 0x1269; af; afi~num-bbs,edges,num-instrs' extracted/main
echo
echo '### deobf.py ๋กœ NOP ํŒจ์น˜ํ•œ ๋’ค ๊ฐ™์€ ํ•จ์ˆ˜'
r2 -qq -e scr.color=0 -e log.level=0 -c 'aaa; s 0x1269; af; afi~num-bbs,edges,num-instrs' extracted/main_clean
echo
echo '### ์›๋ณธ ์•ž๋จธ๋ฆฌ โ€” r2 ๊ฐ€ ํ‘œ์‹œํ•œ ๊ฒน์นจ ํ‘œ์‹(~)๊ณผ CODE XREF'
r2 -qq -e scr.color=0 -e log.level=0 -c 'aaa; s 0x1269; af; pd 12' extracted/main
./cap_r2.sh

radare2 ๋กœ ์›๋ณธ๊ณผ NOP ํŒจ์น˜๋ณธ์˜ ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ๋ถ„์„ํ•œ ๋น„๊ต โ€” ๊ธฐ๋ณธ ๋ธ”๋ก์ด 25๊ฐœ์—์„œ 9๊ฐœ๋กœ, ์—ฃ์ง€๊ฐ€ 27๊ฐœ์—์„œ 11๊ฐœ๋กœ ์ค„๊ณ  ์›๋ณธ์—๋Š” ๊ฒน์นจ ํ‘œ์‹ ๋ฌผ๊ฒฐํ‘œ๊ฐ€ ๋ถ™์–ด ์žˆ๋‹ค
radare2 ๋กœ ์›๋ณธ๊ณผ NOP ํŒจ์น˜๋ณธ์˜ ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ๋ถ„์„ํ•œ ๋น„๊ต โ€” ๊ธฐ๋ณธ ๋ธ”๋ก์ด 25๊ฐœ์—์„œ 9๊ฐœ๋กœ, ์—ฃ์ง€๊ฐ€ 27๊ฐœ์—์„œ 11๊ฐœ๋กœ ์ค„๊ณ  ์›๋ณธ์—๋Š” ๊ฒน์นจ ํ‘œ์‹ ๋ฌผ๊ฒฐํ‘œ๊ฐ€ ๋ถ™์–ด ์žˆ๋‹ค

๊ธฐ๋ณธ ๋ธ”๋ก 25 โ†’ 9, ์—ฃ์ง€ 27 โ†’ 11. ๊ฒจ์šฐ ์Šคํ… ๋ช‡ ๊ฐœ ๋“  ํ•จ์ˆ˜ ํ•˜๋‚˜๊ฐ€ ์ด ์ •๋„๋‹ค. r2 ๋Š” 0x1271 ์ค„ ์•ž์— ~ ๋ฅผ ๋ถ™์—ฌ "์ด ๋ช…๋ น์€ ๋‹ค๋ฅธ ๋ช…๋ น๊ณผ ๋ฐ”์ดํŠธ๊ฐ€ ๊ฒน์นœ๋‹ค"๊ณ  ํ‘œ์‹œํ•ด ์ค€๋‹ค.

๊ทธ๋ฆฌ๊ณ  ํ•œ ๊ตฐ๋ฐ๋Š” r2 ๋„ ํ‹€๋ฆฐ๋‹ค. 0x1286 ์˜ ์Šคํ…์€ ๋ฐ”๋กœ ์•ž jmp 0x137a ๋•Œ๋ฌธ์— ์•„๋ฌด๋„ ๋„๋‹ฌํ•˜์ง€ ์•Š๋Š” ์ž๋ฆฌ๋ผ, ๋ถ„๊ธฐ๋ฅผ ๋”ฐ๋ผ์˜ฌ ๋ฐฉ๋ฒ•์ด ์—†์–ด r2 ์—ญ์‹œ ์„ ํ˜•์œผ๋กœ ์ฝ์–ด sar edi, 0xc9 ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฑ‰๋Š”๋‹ค. ์žฌ๊ท€ ํ•˜๊ฐ•๋„ ๋งŒ๋Šฅ์€ ์•„๋‹ˆ๋‹ค.



๐Ÿฉน ๋˜์‚ด๋ฆฌ๊ธฐ โ€” ๊ธธ์ด๋ฅผ ์œ ์ง€ํ•œ ์ฑ„ ๋ฎ๋Š”๋‹ค

ํ•ด๊ฒฐ์€ ๊ฐ„๋‹จํ•˜๋‹ค. ์Šคํ… 5๋ฐ”์ดํŠธ๋ฅผ nop 5๊ฐœ๋กœ ๋ฎ์œผ๋ฉด ๋œ๋‹ค.

๊ธธ์ด๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ฒŒ ํ•ต์‹ฌ์ด๋‹ค. ์ง€์šฐ๋ฉด ์•ˆ ๋œ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 2 โ€” ๊ทธ๋ƒฅ ์ง€์šฐ๋ฉด ๋˜์ง€ ์•Š๋‚˜ (์•ˆ ๋œ๋‹ค)

์ฒ˜์Œ์—” ์Šคํ…์„ ์‚ญ์ œํ•ด ์ฝ”๋“œ๋ฅผ ์งง๊ฒŒ ๋งŒ๋“ค ์ƒ๊ฐ์„ ํ–ˆ๋‹ค. ์‹ค์ œ๋กœ ํ•ด ๋ณด๋ฉด ์™œ ์•ˆ ๋˜๋Š”์ง€ ๋ฐ”๋กœ ๋‚˜์˜จ๋‹ค.

#!/usr/bin/env python3
"""์Šคํ…์„ '์ง€์›Œ์„œ' ์—†์• ๋ฉด ์™œ ์•ˆ ๋˜๋Š”์ง€ ์ง์ ‘ ํ™•์ธํ•˜๋Š” ๋ฐ˜๋ก€.
 
.text(0x1000~0x18d9) ์•ˆ์˜ 5๋ฐ”์ดํŠธ ์Šคํ…๋งŒ ์‚ญ์ œํ•˜๊ณ , ์ค„์–ด๋“  ๋งŒํผ ์„น์…˜ ๋์„ 0xcc ๋กœ ์ฑ„์›Œ
ํŒŒ์ผ ๋ ˆ์ด์•„์›ƒ์€ ๊ทธ๋Œ€๋กœ ๋‘”๋‹ค. ๊ทธ๋ž˜๋„ ๊ฒฐ๊ณผ๋Š” ๋ชป ์“ด๋‹ค โ€” ๋’ค์ชฝ ์ฝ”๋“œ๊ฐ€ ํ†ต์งธ๋กœ ์•ž๋‹น๊ฒจ์ง€๋Š”๋ฐ
jmp/jle ์˜ ์˜คํผ๋žœ๋“œ๋Š” ํŒŒ์ผ์— ๋ฐ•ํžŒ ์ƒ๋Œ€ ๋ณ€์œ„๋ผ ๊ทธ๋Œ€๋กœ ๋‚จ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
๊ธธ์ด๋ฅผ ์œ ์ง€ํ•œ ์ฑ„ NOP ์œผ๋กœ ๋ฎ์–ด์•ผ ํ•˜๋Š” ์ด์œ ๊ฐ€ ์ด๊ฒƒ์ด๋‹ค.
"""
from pathlib import Path
 
STUB = bytes.fromhex("ebffc1ffc9")
TEXT_LO, TEXT_HI = 0x1000, 0x18D9      # readelf -S ์˜ .init ~ .fini ๋
HERE = Path(__file__).resolve().parent
 
data = HERE.joinpath("extracted/main").read_bytes()
text = data[TEXT_LO:TEXT_HI]
shrunk = text.replace(STUB, b"")
removed = len(text) - len(shrunk)
patched = data[:TEXT_LO] + shrunk + b"\xcc" * removed + data[TEXT_HI:]
assert len(patched) == len(data)
 
dst = HERE / "extracted" / "main_deleted"
dst.write_bytes(patched)
print(f".text ์—์„œ {removed} bytes ์‚ญ์ œ, ๊ฐ™์€ ํฌ๊ธฐ๋งŒํผ 0xcc ๋กœ ๋’ค๋ฅผ ์ฑ„์›€ -> {dst.name}")
print(f"ํŒŒ์ผ ํฌ๊ธฐ {len(patched)} ์œ ์ง€ (์„น์…˜ ํ—ค๋”ยทํ”„๋กœ๊ทธ๋žจ ํ—ค๋”๋Š” ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์•˜๋‹ค)")
python3 deobf_bad.py && objdump -d -M intel --start-address=0x1269 --stop-address=0x12b7 extracted/main_deleted | tail -n +7

์Šคํ…์„ ์‚ญ์ œํ•œ ๋ฐ˜๋ก€ ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ objdump โ€” ๋ช…๋ น์€ ๊น”๋”ํ•ด ๋ณด์ด์ง€๋งŒ lea ์˜ rip ์ƒ๋Œ€ ์ฐธ์กฐ๊ฐ€ 0x4022 ๊ฐ€ ์•„๋‹ˆ๋ผ 0x4013 ์„ ๊ฐ€๋ฆฌํ‚ค๊ณ  ๋’ค์ชฝ์€ rex.W ์™€ .byte ๋กœ ๊นจ์ง„๋‹ค
์Šคํ…์„ ์‚ญ์ œํ•œ ๋ฐ˜๋ก€ ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ objdump โ€” ๋ช…๋ น์€ ๊น”๋”ํ•ด ๋ณด์ด์ง€๋งŒ lea ์˜ rip ์ƒ๋Œ€ ์ฐธ์กฐ๊ฐ€ 0x4022 ๊ฐ€ ์•„๋‹ˆ๋ผ 0x4013 ์„ ๊ฐ€๋ฆฌํ‚ค๊ณ  ๋’ค์ชฝ์€ rex.W ์™€ .byte ๋กœ ๊นจ์ง„๋‹ค

๋ฌด์„œ์šด ๊ฑด ๊ฒ‰๋ณด๊ธฐ์—” ๋ฉ€์ฉกํ•˜๋‹ค๋Š” ์ ์ด๋‹ค. ์•ž๋ถ€๋ถ„ ๋ช…๋ น์€ ์˜คํžˆ๋ ค ๋” ๊นจ๋—ํ•ด ๋ณด์ธ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ lea rax,[rip+0x2d7c] ์˜ ์ฃผ์„์ด # 4022 ๊ฐ€ ์•„๋‹ˆ๋ผ # 4013 ์ด๋‹ค. ๋ณ€์œ„ 0x2d7c ๋Š” ํŒŒ์ผ์— ๋ฐ•ํžŒ ์ƒ์ˆ˜๋ผ ๊ทธ๋Œ€๋กœ์ธ๋ฐ ๋ช…๋ น์ด 15๋ฐ”์ดํŠธ ์•ž๋‹น๊ฒจ์กŒ์œผ๋‹ˆ, ์‹คํšจ ์ฃผ์†Œ๊ฐ€ ๊ทธ๋งŒํผ ์–ด๊ธ‹๋‚œ ๊ฒƒ์ด๋‹ค.

์ด ์ƒํƒœ๋กœ ๋ถ„์„ํ–ˆ์œผ๋ฉด ์—‰๋šฑํ•œ ์ฃผ์†Œ์—์„œ ํ…Œ์ด๋ธ”์„ ์ฝ๊ณ  "๋ฐ์ดํ„ฐ๊ฐ€ ์ด์ƒํ•œ๋ฐ" ํ•˜๋ฉฐ ํ•œ์ฐธ์„ ํ—ค๋งธ์„ ๊ฒƒ์ด๋‹ค. ์กฐ๊ธˆ ๋” ๋’ค๋กœ ๊ฐ€๋ฉด rex.W / .byte 0x8d ์ฒ˜๋Ÿผ ์•„์˜ˆ ๋””์ฝ”๋”ฉ์ด ๊นจ์ง€๊ธฐ๋„ ํ•œ๋‹ค.

์ฐธ๊ณ ๋กœ .text ๋ฐ–๊นŒ์ง€ ์‹ธ์žก์•„ ์‚ญ์ œํ•˜๋ฉด ์„น์…˜ ํ—ค๋” ์˜คํ”„์…‹์ด ๋ฐ€๋ ค objdump ๊ฐ€ ์•„์˜ˆ file format not recognized ๋ฅผ ๋ฑ‰๋Š”๋‹ค. ๊ทธ๊ฑด ์˜คํžˆ๋ ค ํ‹ฐ๊ฐ€ ๋‚˜์„œ ๋‚ซ๋‹ค. ์œ„์ฒ˜๋Ÿผ ์กฐ์šฉํžˆ 15๋ฐ”์ดํŠธ๋งŒ ์–ด๊ธ‹๋‚˜๋Š” ์ชฝ์ด ํ›จ์”ฌ ์œ„ํ—˜ํ•˜๋‹ค.

๊ทธ๋ž˜์„œ ์‚ญ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฎ์–ด์“ฐ๊ธฐ๋‹ค. ์ƒ๋Œ€ ๋ถ„๊ธฐ ๋ณ€์œ„๊ฐ€ ์ „๋ถ€ ๊ทธ๋Œ€๋กœ ์‚ด์•„ ์žˆ์œผ๋‹ˆ, NOP ์œผ๋กœ ๋ฎ์€ ์‚ฌ๋ณธ์„ ๋‹ค์‹œ objdump ์— ๋„ฃ๊ธฐ๋งŒ ํ•˜๋ฉด ์›๋ž˜ ์ฝ”๋“œ๊ฐ€ ๋‚˜์˜จ๋‹ค.

#!/usr/bin/env python3
"""Vernichtet โ€” anti-disassembly ์Šคํ… ์ œ๊ฑฐ.
 
main ์˜ .text ์ „์ฒด์— 5๋ฐ”์ดํŠธ ์žก์Œ `eb ff c1 ff c9` ๊ฐ€ ๋ฐ•ํ˜€ ์žˆ๋‹ค.
  eb ff        jmp $+1        ; ์ž๊ธฐ ์ž์‹ ์˜ ๋‹ค์Œ ๋ฐ”์ดํŠธ(=์˜คํผ๋žœ๋“œ 0xff)๋กœ ์ ํ”„
  ff c1        inc ecx        ; ์ ํ”„ํ•ด์„œ ๋“ค์–ด์˜จ ๋’ค ์‹ค์ œ๋กœ ์‹คํ–‰๋˜๋Š” ๋ช…๋ น
  ff c9        dec ecx        ; ๋˜๋Œ๋ฆฐ๋‹ค -> ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š” no-op
์„ ํ˜• ์Šค์œ• ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ(objdump)๋Š” 0xeb 0xff ๋ฅผ ํ•˜๋‚˜์˜ ๋ช…๋ น์œผ๋กœ ์ฝ์–ด ๋ฒ„๋ ค
๊ทธ ๋’ค 3๋ฐ”์ดํŠธ๋ฅผ ์—‰๋šฑํ•œ ๋ช…๋ น(`sar edi,0xc9`)์œผ๋กœ ํ•ด์„ํ•˜๊ณ , ๊ทธ ์˜ค์ •๋ ฌ์ด
๋ฐ”๋กœ ๋’ค ์‹ค์ œ ๋ช…๋ น๊นŒ์ง€ ๊ฐ‰์•„๋จน๋Š”๋‹ค.
 
๊ธธ์ด๋ฅผ ์œ ์ง€ํ•œ ์ฑ„ NOP 5๊ฐœ๋กœ ๋ฎ์œผ๋ฉด ์ƒ๋Œ€ ๋ถ„๊ธฐ ์˜คํ”„์…‹์ด ์ „๋ถ€ ๊ทธ๋Œ€๋กœ ์‚ด์•„ ์žˆ์–ด
๊ทธ๋ƒฅ ๋‹ค์‹œ objdump ํ•˜๋ฉด ์›๋ž˜ ์ฝ”๋“œ๊ฐ€ ๋‚˜์˜จ๋‹ค.
"""
import sys
from pathlib import Path
 
STUB = bytes.fromhex("ebffc1ffc9")   # jmp $+1 ; inc ecx ; dec ecx
NOPS = b"\x90" * len(STUB)
 
src = Path(sys.argv[1] if len(sys.argv) > 1 else "extracted/main")
dst = Path(sys.argv[2] if len(sys.argv) > 2 else "extracted/main_clean")
 
data = bytearray(src.read_bytes())
count = 0
i = 0
while True:
    i = data.find(STUB, i)
    if i < 0:
        break
    data[i:i + len(STUB)] = NOPS
    count += 1
    i += len(STUB)
 
dst.write_bytes(bytes(data))
dst.chmod(0o755)
print(f"patched {count} stubs ({count * len(STUB)} bytes) -> {dst}")
python3 deobf.py && objdump -d -M intel --start-address=0x1269 --stop-address=0x12b6 extracted/main_clean | grep -v nop$

deobf.py ๋กœ ์Šคํ… 97๊ฐœ 485๋ฐ”์ดํŠธ๋ฅผ NOP ์œผ๋กœ ๋ฎ์€ ๋’ค objdump ๊ฒฐ๊ณผ โ€” jmp ์™€ sar edi,0xc9 ๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ  ํ•จ์ˆ˜ ๋„์ž…๋ถ€๊ฐ€ rbp ์„ธํŒ…๋ถ€ํ„ฐ lea rax rip+0x2d7c ๊นŒ์ง€ ์—ฐ์†์œผ๋กœ ์ฝํžŒ๋‹ค
deobf.py ๋กœ ์Šคํ… 97๊ฐœ 485๋ฐ”์ดํŠธ๋ฅผ NOP ์œผ๋กœ ๋ฎ์€ ๋’ค objdump ๊ฒฐ๊ณผ โ€” jmp ์™€ sar edi,0xc9 ๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ  ํ•จ์ˆ˜ ๋„์ž…๋ถ€๊ฐ€ rbp ์„ธํŒ…๋ถ€ํ„ฐ lea rax rip+0x2d7c ๊นŒ์ง€ ์—ฐ์†์œผ๋กœ ์ฝํžŒ๋‹ค

grep -v nop$ ๋กœ NOP ์ค„๋งŒ ๊ฑท์–ด๋‚ด๋ฉด ์›๋ž˜ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ฝ‘์•˜์„ ์ฝ”๋“œ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋‚จ๋Š”๋‹ค. ์ด์ œ๋ถ€ํ„ฐ๋Š” ํ‰๋ฒ”ํ•œ ๋ฆฌ๋ฒ„์‹ฑ์ด๋‹ค.



๐Ÿ” ์ฒซ ๋ฒˆ์งธ ๊ฒ€์‚ฌ โ€” .data ์— ๋ฐ•ํžŒ ํžŒํŠธ ํ…Œ์ด๋ธ”

main ์€ ์ •๋‹ต ํŒŒ์ผ์„ ์ฝ์–ด ๋‘ ํ•จ์ˆ˜์— ์ฐจ๋ก€๋กœ ๋„˜๊ธด๋‹ค. ๋จผ์ € 0x1269.

์ฃผ์†Œ๋งŒ ์žˆ๋Š” ์–ด์…ˆ์€ ๋ˆˆ์ด ์•„ํ”„๋‹ˆ sed ๋กœ ์ฃผ์„์„ ์–น์—ˆ๋‹ค. ์‹ค์ œ ์ถœ๋ ฅ์— ์„ค๋ช…๋งŒ ๋ถ™์ด๋Š” ๊ฒƒ์ด๋ผ ํ™”๋ฉด์€ ์ง„์งœ ๊ทธ๋Œ€๋กœ๋‹ค.

s|.*2d7c.*|&    <-- ํžŒํŠธ ํ…Œ์ด๋ธ” 0x4022 = i๋ฒˆ์งธ ์—”ํŠธ๋ฆฌ์˜ "๊ฐ’"|
s|.*2d45.*|&    <-- 0x4021 = "ํ–‰"|
s|.*2d0a.*|&    <-- 0x4020 = "์—ด"|
s|.*je     136b.*|&    <-- ๊ฐ’์ด 0 ์ด๋ฉด ํžŒํŠธ ์—†์Œ, ๋‹ค์Œ ์—”ํŠธ๋ฆฌ๋กœ|
s|.*shl    eax,0x4.*|&              <-- ํ–‰ * 16|
s|.*sub    eax,edx.*|&              <-- ... - ํ–‰ = ํ–‰ * 15|
s|.*add    eax,esi.*|&              <-- ์—ด + ํ–‰*15 = ๊ฒฉ์ž ์ธ๋ฑ์Šค|
s|.*cmp    sil,al.*|&               <-- buf ์˜ ๊ทธ ์นธ์ด ํžŒํŠธ ๊ฐ’๊ณผ ๊ฐ™์€๊ฐ€|
s|.*mov    eax,0x0.*|&      <-- ํ•˜๋‚˜๋ผ๋„ ํ‹€๋ฆฌ๋ฉด ์ฆ‰์‹œ 0 ๋ฐ˜ํ™˜|
s|.*,0xe0.*|&        <-- i <= 224, ์ฆ‰ ์—”ํŠธ๋ฆฌ 225๊ฐœ|
objdump -d -M intel --start-address=0x1269 --stop-address=0x138e extracted/main_clean | grep -v nop$ | sed -f annot_clues.sed

objdump ๋กœ ๋ณธ 0x1269 ๊ฒ€์‚ฌ ํ•จ์ˆ˜ ์ „๋ฌธ์— sed ๋กœ ์ฃผ์„์„ ์–น์€ ํ™”๋ฉด โ€” 0x4020, 0x4021, 0x4022 ๋ฅผ 3๋ฐ”์ดํŠธ ๊ฐ„๊ฒฉ์œผ๋กœ ์ฝ์–ด ์—ด ๋”ํ•˜๊ธฐ ํ–‰ ๊ณฑํ•˜๊ธฐ 15 ์ธ๋ฑ์Šค์˜ ๊ฐ’์„ ๋น„๊ตํ•˜๋Š” ๋ฃจํ”„๊ฐ€ 225ํšŒ ๋ˆ๋‹ค
objdump ๋กœ ๋ณธ 0x1269 ๊ฒ€์‚ฌ ํ•จ์ˆ˜ ์ „๋ฌธ์— sed ๋กœ ์ฃผ์„์„ ์–น์€ ํ™”๋ฉด โ€” 0x4020, 0x4021, 0x4022 ๋ฅผ 3๋ฐ”์ดํŠธ ๊ฐ„๊ฒฉ์œผ๋กœ ์ฝ์–ด ์—ด ๋”ํ•˜๊ธฐ ํ–‰ ๊ณฑํ•˜๊ธฐ 15 ์ธ๋ฑ์Šค์˜ ๊ฐ’์„ ๋น„๊ตํ•˜๋Š” ๋ฃจํ”„๊ฐ€ 225ํšŒ ๋ˆ๋‹ค

shl eax,0x4 ํ•˜๊ณ  sub eax,edx ํ•˜๋Š” ๊ฑด x*16 - x = x*15 ๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ 15 ๊ณฑํ•˜๊ธฐ๋ฅผ ์ด๋ ‡๊ฒŒ ์ชผ๊ฐ ๋‹ค. 15 ๋ผ๋Š” ์ˆซ์ž๊ฐ€ ์—ฌ๊ธฐ์„œ ์ฒ˜์Œ ๋‚˜์˜จ๋‹ค.

์ •๋ฆฌํ•˜๋ฉด ์ด๋ ‡๋‹ค.

// 0x1269 โ€” .data 0x4020 ๋ถ€ํ„ฐ (์—ด, ํ–‰, ๊ฐ’) 3๋ฐ”์ดํŠธ ร— 225 ์—”ํŠธ๋ฆฌ
int check_clues(unsigned char *buf)
{
    for (int i = 0; i <= 224; i++) {
        unsigned char val = TBL[3*i + 2];
        if (val == 0)                    // ๊ฐ’ 0 = ํžŒํŠธ ์—†๋Š” ์ž๋ฆฌ
            continue;
        unsigned char row = TBL[3*i + 1];
        unsigned char col = TBL[3*i + 0];
        if (buf[col + 15*row] != val)
            return 0;
    }
    return 1;
}

.data ๋Š” 0x4000 ์—์„œ ์‹œ์ž‘ํ•ด ํฌ๊ธฐ๊ฐ€ 0x2c3 ์ด๋‹ค. ํ…Œ์ด๋ธ”์€ 0x4020 ๋ถ€ํ„ฐ 3 ร— 225 = 675 ๋ฐ”์ดํŠธ๋‹ˆ ์ •ํ™•ํžˆ 0x42c3 ์—์„œ ๋๋‚œ๋‹ค. ์„น์…˜ ๋๊ณผ ๋”ฑ ๋งž๋ฌผ๋ฆฐ๋‹ค โ€” ์ž๋ฆฌ๋ฅผ ์ œ๋Œ€๋กœ ์งš์—ˆ๋‹ค๋Š” ๋œป์ด๋‹ค.


๐Ÿ” ๋‘ ๋ฒˆ์งธ ๊ฒ€์‚ฌ โ€” ๊ฒฝ๋กœ๊ฐ€ ์ด์–ด์ง€๋Š”๊ฐ€

๋‘ ๋ฒˆ์งธ ํ•จ์ˆ˜ 0x138e ๋Š” ํ›จ์”ฌ ๊ธธ๋‹ค. ์„ธ ํ† ๋ง‰์œผ๋กœ ๋‚˜๋ˆ  ๋ณธ๋‹ค. ๋จผ์ € ์‹œ์ž‘์  ์ฐพ๊ธฐ.

s|.*QWORD PTR \[rbp-0x28\],rdi.*|&    <-- ๋‹ต์•ˆ ๋ฒ„ํผ 225๋ฐ”์ดํŠธ|
s|.*BYTE PTR \[rbp-0x1b\],0x0.*|&    <-- cx = 0 (ํ˜„์žฌ ์—ด)|
s|.*BYTE PTR \[rbp-0x1a\],0x0.*|&    <-- cy = 0 (ํ˜„์žฌ ํ–‰)|
s|.*DWORD PTR \[rbp-0x14\],0x0.*|&    <-- row = 0|
s|.*shl    eax,0x4.*|&              <-- row * 16|
s|.*sub    eax,edx.*|&              <-- ... - row = row * 15|
s|.*add    eax,esi.*|&              <-- row*15 + col|
s|.*cmp    al,0x1.*|&               <-- ์ด ์นธ์˜ ๊ฐ’์ด 1 ์ธ๊ฐ€|
s|.*BYTE PTR \[rbp-0x1b\],al.*|&    <-- cx = col|
s|.*BYTE PTR \[rbp-0x1a\],al.*|&    <-- cy = row|
s|.*\[rbp-0x10\],0xe$|&      <-- col <= 14|
s|.*\[rbp-0x14\],0xe$|&      <-- row <= 14|
s|.*\[rbp-0xc\],0x1$|&   <-- k = 1 ๋ถ€ํ„ฐ ์‹œ์ž‘|
objdump -d -M intel --start-address=0x138e --stop-address=0x1443 extracted/main_clean | grep -v nop$ | sed -f annot_path_find.sed

objdump ๋กœ ๋ณธ 0x138e ํ•จ์ˆ˜ ์•ž๋ถ€๋ถ„์— ์ฃผ์„์„ ์–น์€ ํ™”๋ฉด โ€” row ์™€ col ์„ ๊ฐ๊ฐ 0์—์„œ 14๊นŒ์ง€ ๋„๋Š” ์ด์ค‘ ๋ฃจํ”„๊ฐ€ ๊ฐ’์ด 1์ธ ์นธ์„ ์ฐพ์•„ cx, cy ์— ์ €์žฅํ•œ๋‹ค
objdump ๋กœ ๋ณธ 0x138e ํ•จ์ˆ˜ ์•ž๋ถ€๋ถ„์— ์ฃผ์„์„ ์–น์€ ํ™”๋ฉด โ€” row ์™€ col ์„ ๊ฐ๊ฐ 0์—์„œ 14๊นŒ์ง€ ๋„๋Š” ์ด์ค‘ ๋ฃจํ”„๊ฐ€ ๊ฐ’์ด 1์ธ ์นธ์„ ์ฐพ์•„ cx, cy ์— ์ €์žฅํ•œ๋‹ค

row ๋„ col ๋„ 0๋ถ€ํ„ฐ 14๊นŒ์ง€, ์ธ๋ฑ์Šค๋Š” row*15 + col. 15ร—15 ๊ฒฉ์ž๊ฐ€ ํ™•์ •๋œ๋‹ค. ๊ฐ’์ด 1 ์ธ ์นธ์„ ์ฐพ์•„ ํ˜„์žฌ ์œ„์น˜ (cx, cy) ๋กœ ์žก๋Š”๋‹ค.

๊ทธ๋‹ค์Œ์ด ๊ฒฝ๊ณ„ ํด๋žจํ”„๋‹ค.

s|.*cmp    BYTE PTR \[rbp-0x1b\],0x0.*|&    <-- cx > 0 ์ธ๊ฐ€|
s|.*cmp    BYTE PTR \[rbp-0x1b\],0xd.*|&    <-- cx <= 13 ์ธ๊ฐ€|
s|.*cmp    BYTE PTR \[rbp-0x1a\],0x0.*|&    <-- cy > 0 ์ธ๊ฐ€|
s|.*cmp    BYTE PTR \[rbp-0x1a\],0xd.*|&    <-- cy <= 13 ์ธ๊ฐ€|
s|.*BYTE PTR \[rbp-0x19\],al.*|&    <-- x0 = ์™ผ์ชฝ ๊ฒฝ๊ณ„|
s|.*BYTE PTR \[rbp-0x18\],al.*|&    <-- x1 = ์˜ค๋ฅธ์ชฝ ๊ฒฝ๊ณ„|
s|.*BYTE PTR \[rbp-0x17\],al.*|&    <-- y0 = ์œ„์ชฝ ๊ฒฝ๊ณ„|
s|.*BYTE PTR \[rbp-0x16\],al.*|&    <-- y1 = ์•„๋ž˜์ชฝ ๊ฒฝ๊ณ„|
s|.*BYTE PTR \[rbp-0x15\],0x0.*|&    <-- found = 0|
s|.*\[rbp-0xc\],0x1$|&   <-- k = 1 ๋ถ€ํ„ฐ ์‹œ์ž‘|
objdump -d -M intel --start-address=0x1443 --stop-address=0x14fd extracted/main_clean | grep -v nop$ | sed -f annot_path_clamp.sed

objdump ๋กœ ๋ณธ ๊ฒฝ๊ณ„ ํด๋žจํ”„ ๊ตฌ๊ฐ„ โ€” cx ์™€ cy ๋ฅผ ๊ฐ๊ฐ 0 ์ดˆ๊ณผ์ธ์ง€ 13 ์ดํ•˜์ธ์ง€ ๊ฒ€์‚ฌํ•ด x0, x1, y0, y1 ๋„ค ๊ฒฝ๊ณ„๊ฐ’์„ ๋งŒ๋“ ๋‹ค
objdump ๋กœ ๋ณธ ๊ฒฝ๊ณ„ ํด๋žจํ”„ ๊ตฌ๊ฐ„ โ€” cx ์™€ cy ๋ฅผ ๊ฐ๊ฐ 0 ์ดˆ๊ณผ์ธ์ง€ 13 ์ดํ•˜์ธ์ง€ ๊ฒ€์‚ฌํ•ด x0, x1, y0, y1 ๋„ค ๊ฒฝ๊ณ„๊ฐ’์„ ๋งŒ๋“ ๋‹ค

cx-1, cx+1, cy-1, cy+1 ์„ ๊ฒฉ์ž ์•ˆ์œผ๋กœ ๋ˆŒ๋Ÿฌ ๋‹ด๋Š”๋‹ค. ๋Œ€๊ฐ์„ ๊นŒ์ง€ ํฌํ•จํ•œ 8๋ฐฉํ–ฅ ์ด์›ƒ์„ ๋งŒ๋“ค๋ ค๋Š” ๊ฒƒ์ด๋‹ค.

๋งˆ์ง€๋ง‰์ด ์‹ค์ œ ํƒ์ƒ‰์ด๋‹ค.

s|.*movsx  eax,BYTE PTR \[rbp-0x17\].*|&    <-- r = y0 ๋ถ€ํ„ฐ|
s|.*movsx  eax,BYTE PTR \[rbp-0x19\].*|&    <-- c = x0 ๋ถ€ํ„ฐ|
s|.*movsx  eax,BYTE PTR \[rbp-0x1a\].*|&    <-- cy ์™€ ๋น„๊ต|
s|.*movsx  eax,BYTE PTR \[rbp-0x1b\].*|&    <-- cx ์™€ ๋น„๊ต (์ž๊ธฐ ์ž์‹  ์นธ์€ ๊ฑด๋„ˆ๋›ด๋‹ค)|
s|.*shl    eax,0x4.*|&              <-- r * 16|
s|.*sub    eax,edx.*|&              <-- ... - r = r * 15|
s|.*add    eax,esi.*|&              <-- r*15 + c|
s|.*add    eax,0x1$|&               <-- ์ฐพ๋Š” ๊ฐ’ = k + 1|
s|.*cmp    edx,eax$|&               <-- ์ด ์ด์›ƒ ์นธ์ด k+1 ์ธ๊ฐ€|
s|.*BYTE PTR \[rbp-0x15\],0x1.*|&    <-- found = 1, ์—ฌ๊ธฐ๋กœ ์ด๋™|
s|.*movsx  eax,BYTE PTR \[rbp-0x18\].*|&    <-- c <= x1|
s|.*movsx  eax,BYTE PTR \[rbp-0x16\].*|&    <-- r <= y1|
s|.*xor    eax,0x1.*|&              <-- ๋ชป ์ฐพ์•˜์œผ๋ฉด|
s|.*mov    eax,0x0.*|&      <-- 0 ๋ฐ˜ํ™˜ (๊ฒฝ๋กœ ๋Š๊น€)|
s|.*,0xe0$|&        <-- k <= 224, ์ฆ‰ 225 ๊นŒ์ง€ ์ด์–ด์ ธ์•ผ ํ•œ๋‹ค|
s|.*mov    eax,0x1.*|&      <-- ๋๊นŒ์ง€ ์ด์–ด์กŒ๋‹ค|
objdump -d -M intel --start-address=0x14fd --stop-address=0x15f5 extracted/main_clean | grep -v nop$ | sed -f annot_path_scan.sed

objdump ๋กœ ๋ณธ 8์ด์›ƒ ํƒ์ƒ‰ ๊ตฌ๊ฐ„ โ€” y0์—์„œ y1, x0์—์„œ x1 ๊นŒ์ง€ ํ›‘์œผ๋ฉฐ ์ž๊ธฐ ์นธ์€ ๊ฑด๋„ˆ๋›ฐ๊ณ  k+1 ๊ฐ’์„ ๊ฐ€์ง„ ์ด์›ƒ์„ ์ฐพ์•„ ํ˜„์žฌ ์œ„์น˜๋ฅผ ์˜ฎ๊ธด๋‹ค
objdump ๋กœ ๋ณธ 8์ด์›ƒ ํƒ์ƒ‰ ๊ตฌ๊ฐ„ โ€” y0์—์„œ y1, x0์—์„œ x1 ๊นŒ์ง€ ํ›‘์œผ๋ฉฐ ์ž๊ธฐ ์นธ์€ ๊ฑด๋„ˆ๋›ฐ๊ณ  k+1 ๊ฐ’์„ ๊ฐ€์ง„ ์ด์›ƒ์„ ์ฐพ์•„ ํ˜„์žฌ ์œ„์น˜๋ฅผ ์˜ฎ๊ธด๋‹ค

์ „์ฒด๋ฅผ C ๋กœ ์˜ฎ๊ธฐ๋ฉด ์ด๋ ‡๊ฒŒ ๋œ๋‹ค.

// 0x138e
int check_path(unsigned char *buf)
{
    signed char cx = 0, cy = 0;
 
    for (int row = 0; row <= 14; row++)          // ๊ฐ’ 1 ์ด ์žˆ๋Š” ์นธ์„ ์ฐพ๋Š”๋‹ค
        for (int col = 0; col <= 14; col++)
            if (buf[row*15 + col] == 1) { cx = col; cy = row; }
 
    for (int k = 1; k <= 224; k++) {
        signed char x0 = (cx >  0) ? cx - 1 : cx;   // 8๋ฐฉํ–ฅ ์ด์›ƒ ๋ฒ”์œ„
        signed char x1 = (cx <= 13) ? cx + 1 : cx;
        signed char y0 = (cy >  0) ? cy - 1 : cy;
        signed char y1 = (cy <= 13) ? cy + 1 : cy;
        char found = 0;
 
        for (int r = y0; r <= y1; r++)
            for (int c = x0; c <= x1; c++) {
                if (r == cy && c == cx) continue;   // ์ž๊ธฐ ์นธ์€ ์ œ์™ธ
                if (buf[r*15 + c] == k + 1) { found = 1; cx = c; cy = r; }
            }
 
        if (!found) return 0;                       // ๊ฒฝ๋กœ๊ฐ€ ๋Š๊ธฐ๋ฉด ์‹คํŒจ
    }
    return 1;
}

๊ทธ๋ฆฌ๊ณ  main ์ด ์ด ๋‘˜์„ ๋ฌถ๋Š”๋‹ค.

// 0x15f5
int main(int argc, char **argv)
{
    if (argc <= 1) { puts("Usage ./main <answer file>"); return 0; }
 
    FILE *f = fopen(argv[1], "rb");
    if (!f) { puts("File Not Found"); return 0; }
 
    fseek(f, 0, SEEK_END);
    long size = ftell(f);
    rewind(f);
    if (size != 0xe1) { puts("Wrong answer."); return 0; }   // ์ •ํ™•ํžˆ 225 ๋ฐ”์ดํŠธ
 
    unsigned char *buf = malloc(size);
    if (fread(buf, 1, size, f) != size) { puts("Fread failed"); return 0; }
 
    if (!check_clues(buf)) { puts("Wrong answer."); return 0; }
    if (!check_path(buf))  { puts("Wrong answer."); return 0; }
 
    char cmd[0x110];
    sprintf(cmd, "bash -c \"echo DH{$(sha256sum '%s' | awk '{print $1}')}\"", argv[1]);
    puts("Correct!");
    system(cmd);
    return 0;
}

ํŒŒ์ผ ํฌ๊ธฐ๊ฐ€ 0xe1 = 225 ๋กœ ๋ชป ๋ฐ•ํ˜€ ์žˆ๋‹ค. 15ร—15 = 225 ์ด๊ณ , ์ฑ„์›Œ์•ผ ํ•  ๊ฐ’์€ 1๋ถ€ํ„ฐ 225 ๊นŒ์ง€. ํ•œ ์นธ์ด ์ •ํ™•ํžˆ ํ•œ ๋ฐ”์ดํŠธ๊ณ  225 ๋Š” ํ•œ ๋ฐ”์ดํŠธ์— ๋“ค์–ด๊ฐ„๋‹ค.



๐Ÿงฉ ์ •์ฒด โ€” ์ด๊ฑด Hidato ํผ์ฆ์ด๋‹ค

๋‘ ๊ฒ€์‚ฌ๋ฅผ ํ•ฉ์น˜๋ฉด ๊ทœ์น™์ด ์ด๋ ‡๊ฒŒ ์ •๋ฆฌ๋œ๋‹ค.

  • 15ร—15 ๊ฒฉ์ž์— 1๋ถ€ํ„ฐ 225 ๊นŒ์ง€๋ฅผ ํ•œ ๋ฒˆ์”ฉ ๋†“๋Š”๋‹ค
  • ์—ฐ์†ํ•œ ๋‘ ์ˆ˜๋Š” ์„œ๋กœ 8๋ฐฉํ–ฅ(๋Œ€๊ฐ์„  ํฌํ•จ) ์ด์›ƒ์ด์–ด์•ผ ํ•œ๋‹ค
  • ์ผ๋ถ€ ์นธ์€ ๊ฐ’์ด ๋ฏธ๋ฆฌ ์ •ํ•ด์ ธ ์žˆ๋‹ค

Hidato ๋‹ค. ๋น„์Šทํ•œ ํผ์ฆ๋กœ Numbrix ๊ฐ€ ์žˆ๋Š”๋ฐ ๊ทธ์ชฝ์€ ์ƒํ•˜์ขŒ์šฐ 4๋ฐฉํ–ฅ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค. ์—ฌ๊ธฐ๋Š” ๋Œ€๊ฐ์„ ๋„ ๋˜๋‹ˆ Hidato ์ชฝ์ด๋‹ค.

.data ์—์„œ ํžŒํŠธ๋ฅผ ๊บผ๋‚ด ๊ฒฉ์ž๋กœ ์ฐ์–ด ๋ดค๋‹ค.

#!/usr/bin/env python3
"""Vernichtet โ€” .data ์— ๋ฐ•ํžŒ ํžŒํŠธ ํ…Œ์ด๋ธ”์„ 15x15 ๊ฒฉ์ž๋กœ ๋ณต์›ํ•œ๋‹ค.
 
check_clues(0x1269) ๊ฐ€ ์ฝ๋Š” ๋ฐฐ์—ด์€ 0x4020 ๋ถ€ํ„ฐ 3๋ฐ”์ดํŠธ์”ฉ 225์—”ํŠธ๋ฆฌ:
    b0 = data[0x4020 + 3*i]   -> ์—ด(column)
    b1 = data[0x4021 + 3*i]   -> ํ–‰(row)
    b2 = data[0x4022 + 3*i]   -> ๊ทธ ์นธ์— ๋“ค์–ด๊ฐ€์•ผ ํ•˜๋Š” ๊ฐ’ (0 ์ด๋ฉด ํžŒํŠธ ์—†์Œ)
๊ฒ€์‚ฌ์‹์€ buf[b0 + 15*b1] == b2.
 
.data ๋Š” vaddr 0x4000 = ํŒŒ์ผ ์˜คํ”„์…‹ 0x3000 ์ด๋ฏ€๋กœ 0x4020 -> 0x3020.
"""
from pathlib import Path
 
BIN = Path(__file__).with_name("extracted") / "main"
DATA_VADDR, DATA_OFF = 0x4000, 0x3000
TABLE = 0x4020
N = 15
 
raw = BIN.read_bytes()
tbl = raw[TABLE - DATA_VADDR + DATA_OFF: TABLE - DATA_VADDR + DATA_OFF + 3 * N * N]
assert len(tbl) == 675, len(tbl)
 
grid = [[0] * N for _ in range(N)]
clues = []
for i in range(N * N):
    c, r, v = tbl[3 * i], tbl[3 * i + 1], tbl[3 * i + 2]
    if v == 0:
        continue
    assert 0 <= c < N and 0 <= r < N, (i, c, r, v)
    grid[r][c] = v
    clues.append((r, c, v))
 
print(f"clue entries: {len(clues)} / {N*N}")
print(f"values: min={min(v for _,_,v in clues)} max={max(v for _,_,v in clues)}")
print()
for r in range(N):
    print(" ".join(f"{grid[r][c]:3d}" if grid[r][c] else "  ." for c in range(N)))
python3 parse_clues.py

parse_clues.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” .data 0x4020 ์—์„œ ๊บผ๋‚ธ ํžŒํŠธ 150๊ฐœ๊ฐ€ 15ร—15 ๊ฒฉ์ž๋กœ ์ฐํžˆ๊ณ  ๊ฐ’ ๋ฒ”์œ„๊ฐ€ 1๋ถ€ํ„ฐ 225 ๊นŒ์ง€์ž„์ด ํ™•์ธ๋œ๋‹ค
parse_clues.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” .data 0x4020 ์—์„œ ๊บผ๋‚ธ ํžŒํŠธ 150๊ฐœ๊ฐ€ 15ร—15 ๊ฒฉ์ž๋กœ ์ฐํžˆ๊ณ  ๊ฐ’ ๋ฒ”์œ„๊ฐ€ 1๋ถ€ํ„ฐ 225 ๊นŒ์ง€์ž„์ด ํ™•์ธ๋œ๋‹ค

150์นธ์ด ์ด๋ฏธ ์ฑ„์›Œ์ ธ ์žˆ๊ณ  ๋นˆ์นธ์€ 75์นธ. ๊ฐ’์€ 1๋ถ€ํ„ฐ 225 ๊นŒ์ง€ ๊ณ ๋ฅด๊ฒŒ ํฉ์–ด์ ธ ์žˆ๋‹ค. ๊ฒฉ์ž ๋ชจ์–‘์œผ๋กœ ์ฐ์–ด ๋†“์œผ๋‹ˆ ํผ์ฆ์ฑ…์—์„œ ์˜ค๋ ค ๋ถ™์ธ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 3 โ€” objdump ๋งŒ ๋ณด๊ณ ๋„ ์ฝํ˜€์„œ ์ •๋ฆฌ๋ฅผ ๋ฏธ๋ค˜๋‹ค

์†”์งํžˆ ๊ณ ๋ฐฑํ•˜์ž๋ฉด, ์œ„ ๋กœ์ง์€ NOP ํŒจ์น˜๋ฅผ ํ•˜๊ธฐ ์ „์— ์›๋ณธ objdump ์ถœ๋ ฅ๋งŒ ๋ณด๊ณ  ๋จผ์ € ์ฝ์—ˆ๋‹ค. ์Šคํ…์ด ์ •ํ™•ํžˆ 5๋ฐ”์ดํŠธ์—์„œ ๋๋‚˜ ๊ทธ ๋’ค ๋ช…๋ น๋ถ€ํ„ฐ ๋‹ค์‹œ ๋™๊ธฐํ™”๋˜๊ธฐ ๋•Œ๋ฌธ์—, sar edi,0xc9 ์ค„๋งŒ ๋ˆˆ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๋ฉด ๋กœ์ง ์ž์ฒด๋Š” ๋”ฐ๋ผ๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ž˜์„œ ํ•œ๋™์•ˆ "์“ธ๋ฐ์—†๋Š” ๋ช…๋ น์ด ๋งŽ์ด ๋ผ์–ด ์žˆ๋„ค" ์ •๋„๋กœ๋งŒ ์ƒ๊ฐํ•˜๊ณ  ๋„˜์–ด๊ฐ”๋‹ค. ๋ฌธ์ œ๋ฅผ ์‹ค๊ฐํ•œ ๊ฑด radare2 ๋กœ ํ•จ์ˆ˜ ๊ทธ๋ž˜ํ”„๋ฅผ ๋ณด๋ ค ํ–ˆ์„ ๋•Œ๋‹ค โ€” 25๊ฐœ์งœ๋ฆฌ ๊ธฐ๋ณธ ๋ธ”๋ก ์ŠคํŒŒ๊ฒŒํ‹ฐ๊ฐ€ ๋‚˜์˜ค๋Š” ์ˆœ๊ฐ„ ์ด๊ฒŒ ๋…ธ์ด์ฆˆ๊ฐ€ ์•„๋‹ˆ๋ผ ์˜๋„๋œ ์žฅ์น˜๋ผ๋Š” ๊ฒŒ ๋ถ„๋ช…ํ•ด์กŒ๋‹ค.

๊ตํ›ˆ์ด๋ผ๋ฉด, "์ฝํžˆ๋‹ˆ๊นŒ ๊ดœ์ฐฎ๋‹ค"์™€ "์ •๋ฆฌํ•˜๊ณ  ์ฝ๋Š”๋‹ค"๋Š” ๋‹ค๋ฅด๋‹ค๋Š” ๊ฒƒ. 686์ค„์งœ๋ฆฌ ์ถœ๋ ฅ์—์„œ 26%๋ฅผ ๋ˆˆ์œผ๋กœ ๊ฑธ๋Ÿฌ ๋‚ด๋ฉฐ ์ฝ๋Š” ๋™์•ˆ ๋†“์นœ ๊ฒŒ ์—†์—ˆ๋Š”์ง€๋Š” ์•„๋ฌด๋„ ๋ณด์žฅ ๋ชป ํ•œ๋‹ค. deobf.py ๋Š” ๋‹ค์„ฏ ์ค„์งœ๋ฆฌ ๋ฃจํ”„์ธ๋ฐ ๊ทธ๊ฑธ ๋ฏธ๋ฃฌ ๊ฒŒ ์ด ๋ฌธ์ œ์—์„œ ์ œ์ผ ๋น„ํšจ์œจ์ ์ธ ์„ ํƒ์ด์—ˆ๋‹ค.


๐ŸŽฏ ํ’€์ด โ€” DFS ์— ๋„๋‹ฌ์„ฑ ๊ฐ€์ง€์น˜๊ธฐ ํ•˜๋‚˜

Hidato ๋Š” 1์—์„œ ์ถœ๋ฐœํ•ด ์‚ฌ์Šฌ์„ ๋”ฐ๋ผ๊ฐ€๋Š” ๋ฐฑํŠธ๋ž˜ํ‚น์œผ๋กœ ํ‘ผ๋‹ค. ๋‹ค์Œ ๊ฐ’ k+1 ์˜ ์ž๋ฆฌ๊ฐ€ ํžŒํŠธ๋กœ ์ด๋ฏธ ์ •ํ•ด์ ธ ์žˆ์œผ๋ฉด ์ธ์ ‘ํ•œ์ง€๋งŒ ํ™•์ธํ•˜๊ณ  ๊ทธ ์นธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ณ , ์•ˆ ์ •ํ•ด์ ธ ์žˆ์œผ๋ฉด ๋นˆ ์ด์›ƒ์„ ํ•˜๋‚˜์”ฉ ์‹œ๋„ํ•œ๋‹ค.

์—ฌ๊ธฐ์— ๊ฐ€์ง€์น˜๊ธฐ ํ•˜๋‚˜๋ฅผ ์–น์—ˆ๋‹ค. ๋งค ์Šคํ…๋งˆ๋‹ค ํ˜„์žฌ ์œ„์น˜์—์„œ flood fill ๋กœ "๋‚จ์€ ๋นˆ์นธ ์ „๋ถ€์— ๋‹ฟ์„ ์ˆ˜ ์žˆ๋Š”๊ฐ€"๋ฅผ ๋ณธ๋‹ค. ์‚ฌ์Šฌ์ด ๊ฒฉ์ž๋ฅผ ๋‘ ์กฐ๊ฐ์œผ๋กœ ๊ฐˆ๋ผ ๋†“์œผ๋ฉด ๊ทธ ์ˆœ๊ฐ„ ๋˜๋Œ๋ฆฐ๋‹ค. ์ด๊ฒŒ ์—†์œผ๋ฉด ๋ง‰๋‹ค๋ฅธ ๊ณจ๋ชฉ์„ ๋๊นŒ์ง€ ํŒŒ๊ณ ๋“ค์–ด ์‹œ๊ฐ„์ด ํญ๋ฐœํ•œ๋‹ค.

flag ๋ฅผ ์‹ค์ œ๋กœ ๋ฝ‘์•„๋‚ธ solve.py ์ „๋ฌธ์ด๋‹ค. ํžŒํŠธ๋Š” ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ์•Š๊ณ  ๋งค๋ฒˆ ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ์ง์ ‘ ์ฝ๋Š”๋‹ค.

#!/usr/bin/env python3
"""Vernichtet (DreamHack, Gold 4 / reversing) โ€” ์ž์ฒด ์™„๊ฒฐ ์†”๋ฒ„.
 
๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์š”๊ตฌํ•˜๋Š” ๊ฒƒ:
  1. ๋‹ต์•ˆ ํŒŒ์ผ ํฌ๊ธฐ๊ฐ€ ์ •ํ™•ํžˆ 0xe1 = 225 ๋ฐ”์ดํŠธ
  2. check_clues(0x1269): .data 0x4020 ๋ถ€ํ„ฐ์˜ (์—ด,ํ–‰,๊ฐ’) 3๋ฐ”์ดํŠธ ์—”ํŠธ๋ฆฌ 225๊ฐœ ์ค‘
     ๊ฐ’์ด 0 ์ด ์•„๋‹Œ 150๊ฐœ์— ๋Œ€ํ•ด buf[์—ด + 15*ํ–‰] == ๊ฐ’
  3. check_path(0x138e): ๊ฐ’ 1 ์ด ์žˆ๋Š” ์นธ์—์„œ ์ถœ๋ฐœํ•ด k = 1..224 ์— ๋Œ€ํ•ด
     ๊ฐ’ k+1 ์ด ํ˜„์žฌ ์นธ์˜ 8๋ฐฉํ–ฅ ์ด์›ƒ(ํ‚น ๋ฌด๋ธŒ)์— ์žˆ์–ด์•ผ ํ•œ๋‹ค
=> 15x15 ๊ฒฉ์ž์— 1..225 ๋ฅผ ํ‚น ๋ฌด๋ธŒ๋กœ ์ด์–ด์ง€๊ฒŒ ์ฑ„์šฐ๋Š” Hidato ํผ์ฆ.
 
flag ๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ `sha256sum <๋‹ต์•ˆํŒŒ์ผ>` ๋กœ ๊ณ„์‚ฐํ•ด ์ถœ๋ ฅํ•˜๋ฏ€๋กœ
๋‹ต์•ˆ ํŒŒ์ผ์˜ ๋ฐ”์ดํŠธ ๋ฐฐ์—ด์ด ์ •ํ™•ํžˆ ์ผ์น˜ํ•ด์•ผ ํ•œ๋‹ค.
"""
import hashlib
import sys
from pathlib import Path
 
N = 15
HERE = Path(__file__).resolve().parent
BIN = HERE / "extracted" / "main"
OUT = HERE / "answer.bin"
 
DATA_VADDR, DATA_OFF, TABLE = 0x4000, 0x3000, 0x4020
 
 
def load_clues(path=BIN):
    """๋ฐ”์ด๋„ˆ๋ฆฌ .data ์—์„œ ํžŒํŠธ ํ…Œ์ด๋ธ”์„ ์ง์ ‘ ์ฝ๋Š”๋‹ค (ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ์•Š๋Š”๋‹ค)."""
    raw = path.read_bytes()
    base = TABLE - DATA_VADDR + DATA_OFF
    tbl = raw[base:base + 3 * N * N]
    grid = [[0] * N for _ in range(N)]
    for i in range(N * N):
        col, row, val = tbl[3 * i], tbl[3 * i + 1], tbl[3 * i + 2]
        if val:
            grid[row][col] = val
    return grid
 
 
NB = [(dr, dc) for dr in (-1, 0, 1) for dc in (-1, 0, 1) if (dr, dc) != (0, 0)]
 
 
def solve(grid):
    fixed = {}                      # ๊ฐ’ -> (ํ–‰,์—ด)
    for r in range(N):
        for c in range(N):
            if grid[r][c]:
                fixed[grid[r][c]] = (r, c)
 
    board = [row[:] for row in grid]
    start = fixed[1]
 
    neigh = [[[] for _ in range(N)] for _ in range(N)]
    for r in range(N):
        for c in range(N):
            for dr, dc in NB:
                nr, nc = r + dr, c + dc
                if 0 <= nr < N and 0 <= nc < N:
                    neigh[r][c].append((nr, nc))
 
    def reachable(r, c, k):
        """k..225 ๋ฅผ ์ฑ„์šธ ๋นˆ ์นธ์ด ํ˜„์žฌ ์œ„์น˜์—์„œ ์ „๋ถ€ ๋‹ฟ๋Š”์ง€ (flood fill ๊ฐ€์ง€์น˜๊ธฐ)."""
        need = sum(1 for rr in range(N) for cc in range(N) if board[rr][cc] == 0)
        if need == 0:
            return True
        seen = set()
        stack = [(r, c)]
        got = 0
        while stack:
            cr, cc = stack.pop()
            for nr, nc in neigh[cr][cc]:
                if (nr, nc) in seen:
                    continue
                v = board[nr][nc]
                if v == 0:
                    seen.add((nr, nc))
                    got += 1
                    stack.append((nr, nc))
                elif v > k:
                    seen.add((nr, nc))
                    stack.append((nr, nc))
        return got == need
 
    sys.setrecursionlimit(10000)
 
    def dfs(k, r, c):
        if k == N * N:
            return True
        nxt = k + 1
        if nxt in fixed:
            fr, fc = fixed[nxt]
            if max(abs(fr - r), abs(fc - c)) != 1:
                return False
            return dfs(nxt, fr, fc)
        cands = [(nr, nc) for nr, nc in neigh[r][c] if board[nr][nc] == 0]
        for nr, nc in cands:
            board[nr][nc] = nxt
            if reachable(nr, nc, nxt) and dfs(nxt, nr, nc):
                return True
            board[nr][nc] = 0
        return False
 
    if not dfs(1, *start):
        raise SystemExit("no solution")
    return board
 
 
def verify(board, clues):
    for r in range(N):
        for c in range(N):
            if clues[r][c] and board[r][c] != clues[r][c]:
                return f"ํžŒํŠธ ๋ถˆ์ผ์น˜ ({r},{c})"
    if sorted(v for row in board for v in row) != list(range(1, N * N + 1)):
        return "1..225 ์ˆœ์—ด์ด ์•„๋‹ˆ๋‹ค"
    pos = {}
    for r in range(N):
        for c in range(N):
            pos[board[r][c]] = (r, c)
    for k in range(1, N * N):
        (r1, c1), (r2, c2) = pos[k], pos[k + 1]
        if max(abs(r1 - r2), abs(c1 - c2)) != 1:
            return f"{k} -> {k+1} ์ด ํ‚น ๋ฌด๋ธŒ๊ฐ€ ์•„๋‹ˆ๋‹ค"
    return None
 
 
def main():
    clues = load_clues()
    given = sum(1 for row in clues for v in row if v)
    print(f"[*] .data 0x4020 ํžŒํŠธ {given}๊ฐœ / {N*N}์นธ")
 
    board = solve(clues)
    err = verify(board, clues)
    if err:
        raise SystemExit(f"[!] ๊ฒ€์ฆ ์‹คํŒจ: {err}")
    print("[*] ๊ฒ€์ฆ ํ†ต๊ณผ: ํžŒํŠธ ์ผ์น˜ + 1..225 ์ˆœ์—ด + ์ „ ๊ตฌ๊ฐ„ ํ‚น ๋ฌด๋ธŒ")
    print()
    for r in range(N):
        print(" ".join(f"{board[r][c]:3d}" for c in range(N)))
    print()
 
    blob = bytes(board[r][c] for r in range(N) for c in range(N))
    assert len(blob) == 0xE1
    OUT.write_bytes(blob)
    digest = hashlib.sha256(blob).hexdigest()
    print(f"[*] {OUT.name} ({len(blob)} bytes) ์ €์žฅ")
    print(f"[*] sha256 = {digest}")
    print(f"FLAG: DH{{{digest}}}")
 
 
if __name__ == "__main__":
    main()
python3 solve.py

solve.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ํžŒํŠธ 150๊ฐœ๋ฅผ ์ฝ์–ด 15ร—15 ๊ฒฉ์ž๋ฅผ ์ „๋ถ€ ์ฑ„์šฐ๊ณ  ํžŒํŠธ ์ผ์น˜ยท์ˆœ์—ดยทํ‚น ๋ฌด๋ธŒ ๊ฒ€์ฆ์„ ํ†ต๊ณผํ•œ ๋’ค answer.bin ๊ณผ sha256 ํ•ด์‹œ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค
solve.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ํžŒํŠธ 150๊ฐœ๋ฅผ ์ฝ์–ด 15ร—15 ๊ฒฉ์ž๋ฅผ ์ „๋ถ€ ์ฑ„์šฐ๊ณ  ํžŒํŠธ ์ผ์น˜ยท์ˆœ์—ดยทํ‚น ๋ฌด๋ธŒ ๊ฒ€์ฆ์„ ํ†ต๊ณผํ•œ ๋’ค answer.bin ๊ณผ sha256 ํ•ด์‹œ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค

0.12์ดˆ. ํžŒํŠธ๊ฐ€ 150๊ฐœ๋‚˜ ์ฃผ์–ด์ ธ ์žˆ์–ด ํƒ์ƒ‰ ๊ณต๊ฐ„์ด ๊ฑฐ์˜ ๋‚จ์ง€ ์•Š๋Š”๋‹ค.

๊ฒฝ๋กœ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ง€๋‚˜๊ฐ€๋Š”์ง€ ๊ถ๊ธˆํ•ด์„œ ๊ทธ๋ ค ๋ดค๋‹ค. ํฐ ์ˆซ์ž๊ฐ€ ์›๋ž˜ ์ฃผ์–ด์ ธ ์žˆ๋˜ ์นธ, ์ฃผํ™ฉ ์ˆซ์ž๊ฐ€ ์šฐ๋ฆฌ๊ฐ€ ์ฑ„์šด ์นธ์ด๋‹ค.

#!/usr/bin/env python3
"""answer.bin ์„ 15x15 ๊ฒฉ์ž๋กœ ๊ทธ๋ ค 1 -> 225 ๊ฒฝ๋กœ๋ฅผ ์‹œ๊ฐํ™”ํ•œ๋‹ค.
 
.data ํžŒํŠธ๋กœ ์ด๋ฏธ ์ •ํ•ด์ ธ ์žˆ๋˜ ์นธ(150๊ฐœ)๊ณผ ์šฐ๋ฆฌ๊ฐ€ ์ฑ„์šด ์นธ(75๊ฐœ)์„ ์ƒ‰์œผ๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค.
SVG ๋ฅผ ๋งŒ๋“  ๋’ค rsvg-convert ๋กœ PNG ๋กœ ๊ตฝ๋Š”๋‹ค.
"""
import subprocess
import sys
from pathlib import Path
 
from solve import N, load_clues
 
HERE = Path(__file__).resolve().parent
ANS = HERE / "answer.bin"
SVG = HERE / "hidato_path.svg"
PNG = Path(sys.argv[1]) if len(sys.argv) > 1 else HERE / "hidato_path.png"
 
CELL, PAD = 62, 34
W = H = CELL * N + PAD * 2
 
board = list(ANS.read_bytes())
clues = load_clues()
pos = {board[r * N + c]: (r, c) for r in range(N) for c in range(N)}
 
 
def cx(c):
    return PAD + c * CELL + CELL / 2
 
 
def cy(r):
    return PAD + r * CELL + CELL / 2
 
 
out = [
    f'<?xml version="1.0" encoding="UTF-8"?>',
    f'<svg xmlns="http://www.w3.org/2000/svg" width="{W}" height="{H}" viewBox="0 0 {W} {H}">',
    f'<rect width="{W}" height="{H}" fill="#0d1117"/>',
]
 
# ์นธ ๋ฐฐ๊ฒฝ: ํžŒํŠธ๋กœ ์ฃผ์–ด์ง„ ์นธ์€ ์ง™์€ ํŒŒ๋ž‘, ์šฐ๋ฆฌ๊ฐ€ ์ฑ„์šด ์นธ์€ ์ง™์€ ์ฃผํ™ฉ
for r in range(N):
    for c in range(N):
        given = clues[r][c] != 0
        fill = "#132033" if given else "#2a1a0d"
        stroke = "#30363d"
        out.append(f'<rect x="{PAD + c*CELL}" y="{PAD + r*CELL}" width="{CELL}" height="{CELL}" '
                   f'fill="{fill}" stroke="{stroke}" stroke-width="1"/>')
 
# 1 -> 225 ๊ฒฝ๋กœ: ์ง„ํ–‰๋„์— ๋”ฐ๋ผ ํŒŒ๋ž‘ -> ์ฃผํ™ฉ
for k in range(1, N * N):
    (r1, c1), (r2, c2) = pos[k], pos[k + 1]
    t = (k - 1) / (N * N - 2)
    red = int(0x79 + (0xF0 - 0x79) * t)
    green = int(0xC0 + (0x88 - 0xC0) * t)
    blue = int(0xFF + (0x3E - 0xFF) * t)
    out.append(f'<line x1="{cx(c1):.1f}" y1="{cy(r1):.1f}" x2="{cx(c2):.1f}" y2="{cy(r2):.1f}" '
               f'stroke="rgb({red},{green},{blue})" stroke-width="5" stroke-linecap="round" opacity="0.85"/>')
 
# ์ˆซ์ž
for r in range(N):
    for c in range(N):
        v = board[r * N + c]
        given = clues[r][c] != 0
        color = "#e6edf3" if given else "#f0883e"
        weight = "normal" if given else "bold"
        out.append(f'<text x="{cx(c):.1f}" y="{cy(r)+6:.1f}" font-family="DejaVu Sans Mono, monospace" '
                   f'font-size="19" fill="{color}" font-weight="{weight}" text-anchor="middle">{v}</text>')
 
# ์‹œ์ž‘ยท๋ ํ‘œ์‹œ
for v, col in ((1, "#7ee787"), (N * N, "#f97583")):
    r, c = pos[v]
    out.append(f'<circle cx="{cx(c):.1f}" cy="{cy(r):.1f}" r="26" fill="none" stroke="{col}" stroke-width="3"/>')
 
out.append("</svg>")
SVG.write_text("\n".join(out), encoding="utf-8")
subprocess.run(["rsvg-convert", "-w", str(W), "-h", str(H), str(SVG), "-o", str(PNG)], check=True)
print(f"{SVG.name} -> {PNG}  ({W}x{H})")
print(f"ํžŒํŠธ๋กœ ์ฃผ์–ด์ง„ ์นธ {sum(1 for row in clues for v in row if v)}๊ฐœ(ํฐ ๊ธ€์”จ) / "
      f"์šฐ๋ฆฌ๊ฐ€ ์ฑ„์šด ์นธ {sum(1 for row in clues for v in row if not v)}๊ฐœ(์ฃผํ™ฉ ๊ธ€์”จ)")
python3 draw_path.py /home/jinho/homepage/public/images/blog/dreamhack-vernichtet-writeup/15_hidato_path.png

draw_path.py ๊ฐ€ ๊ทธ๋ฆฐ ์™„์„ฑ ๊ฒฉ์ž โ€” ์™ผ์ชฝ ์•„๋ž˜ ์ดˆ๋ก ์›์ด 1, ๊ฐ€์šด๋ฐ ์œ„์ชฝ ๋ถ„ํ™ ์›์ด 225 ์ด๊ณ  ํŒŒ๋ž‘์—์„œ ์ฃผํ™ฉ์œผ๋กœ ๋ณ€ํ•˜๋Š” ์„ ์ด 225์นธ์„ ํ•œ ๋ถ“์œผ๋กœ ์ž‡๋Š”๋‹ค
draw_path.py ๊ฐ€ ๊ทธ๋ฆฐ ์™„์„ฑ ๊ฒฉ์ž โ€” ์™ผ์ชฝ ์•„๋ž˜ ์ดˆ๋ก ์›์ด 1, ๊ฐ€์šด๋ฐ ์œ„์ชฝ ๋ถ„ํ™ ์›์ด 225 ์ด๊ณ  ํŒŒ๋ž‘์—์„œ ์ฃผํ™ฉ์œผ๋กœ ๋ณ€ํ•˜๋Š” ์„ ์ด 225์นธ์„ ํ•œ ๋ถ“์œผ๋กœ ์ž‡๋Š”๋‹ค

์™ผ์ชฝ ์•„๋ž˜ ์ดˆ๋ก ์›์ด 1, ๊ฒฉ์ž ํ•œ๋ณตํŒ ์กฐ๊ธˆ ์œ„ ๋ถ„ํ™ ์›์ด 225 ๋‹ค. ์‚ฌ์Šฌ ํ•˜๋‚˜๊ฐ€ 225์นธ์„ ๋น ์ง์—†์ด ํ›‘๊ณ  ์ง€๋‚˜๊ฐ„๋‹ค.



๐Ÿš€ ๊ฒ€์ฆ โ€” ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์ง์ ‘ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฑ‰๋Š”๋‹ค

๋งŒ๋“  ํŒŒ์ผ์„ ๋ฌธ์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๊ทธ๋Œ€๋กœ ๋จน์ธ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๊ณ„์‚ฐํ•œ sha256 ๊ณผ ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์ถœ๋ ฅํ•œ ๊ฐ’์ด ๊ฐ™์•„์•ผ ํ•œ๋‹ค.

sha256sum answer.bin && ./extracted/main answer.bin

sha256sum ๊ณผ ๋ฌธ์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ๋‘ ํ•ด์‹œ๊ฐ€ ๊ฐ™๊ณ  ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ Correct! ์™€ ํ•จ๊ป˜ DH ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ์ง์ ‘ ์ถœ๋ ฅํ•œ๋‹ค
sha256sum ๊ณผ ๋ฌธ์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ๋‘ ํ•ด์‹œ๊ฐ€ ๊ฐ™๊ณ  ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ Correct! ์™€ ํ•จ๊ป˜ DH ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ์ง์ ‘ ์ถœ๋ ฅํ•œ๋‹ค

Correct! ์™€ ํ•จ๊ป˜ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋‚˜์˜จ๋‹ค.

DH{e309147b588c517bb4100064d6185e5430ebad23d83e601327c4907bb0232292}

ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋‹ต์•ˆ ํŒŒ์ผ์˜ ํ•ด์‹œ๋ผ๋Š” ๊ฑด, ํ•ด๊ฐ€ ์œ ์ผํ•˜์ง€ ์•Š์œผ๋ฉด ํ”Œ๋ž˜๊ทธ๋„ ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ๋œ๋‹ค๋Š” ๋œป์ด๋‹ค. ๊ทธ๋Ÿด ๋ฆฌ๋Š” ์—†๊ฒ ์ง€๋งŒ ํ™•์ธ์€ ํ•ด ๋ดค๋‹ค. ์ฒซ ํ•ด์—์„œ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ๋ฐฑํŠธ๋ž˜ํ‚น์„ ๋๊นŒ์ง€ ๋Œ๋ ค ์™„์„ฑํ•ด์˜ ๊ฐœ์ˆ˜๋ฅผ ์„ผ๋‹ค.

#!/usr/bin/env python3
"""ํžŒํŠธ 150๊ฐœ๋กœ Hidato ํ•ด๊ฐ€ ์œ ์ผํ•œ์ง€ ์ „์ˆ˜ ํ™•์ธ.
 
flag ๊ฐ€ ๋‹ต์•ˆ ํŒŒ์ผ์˜ sha256 ์ด๋ฏ€๋กœ, ํ•ด๊ฐ€ ๋‘˜ ์ด์ƒ์ด๋ฉด flag ๋„ ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ๋˜์–ด ๋ฒ„๋ฆฐ๋‹ค.
(์‹ค์ œ๋กœ๋Š” ์œ ์ผํ•ด์•ผ ์ •์ƒ) ๋ฐฑํŠธ๋ž˜ํ‚น์œผ๋กœ ํ•ด๋ฅผ ๋๊นŒ์ง€ ์„ธ์–ด ๋ณธ๋‹ค.
"""
import sys
from solve import N, NB, load_clues
 
 
def count(grid, limit=5):
    fixed = {}
    for r in range(N):
        for c in range(N):
            if grid[r][c]:
                fixed[grid[r][c]] = (r, c)
    board = [row[:] for row in grid]
    neigh = [[[(r + dr, c + dc) for dr, dc in NB
               if 0 <= r + dr < N and 0 <= c + dc < N] for c in range(N)] for r in range(N)]
    found = 0
    nodes = 0
 
    def reachable(r, c, k):
        need = sum(1 for rr in range(N) for cc in range(N) if board[rr][cc] == 0)
        if need == 0:
            return True
        seen, stack, got = set(), [(r, c)], 0
        while stack:
            cr, cc = stack.pop()
            for nr, nc in neigh[cr][cc]:
                if (nr, nc) in seen:
                    continue
                v = board[nr][nc]
                if v == 0:
                    seen.add((nr, nc)); got += 1; stack.append((nr, nc))
                elif v > k:
                    seen.add((nr, nc)); stack.append((nr, nc))
        return got == need
 
    def dfs(k, r, c):
        nonlocal found, nodes
        nodes += 1
        if k == N * N:
            found += 1
            return found >= limit
        nxt = k + 1
        if nxt in fixed:
            fr, fc = fixed[nxt]
            if max(abs(fr - r), abs(fc - c)) != 1:
                return False
            return dfs(nxt, fr, fc)
        for nr, nc in neigh[r][c]:
            if board[nr][nc]:
                continue
            board[nr][nc] = nxt
            if reachable(nr, nc, nxt) and dfs(nxt, nr, nc):
                board[nr][nc] = 0
                return True
            board[nr][nc] = 0
        return False
 
    sys.setrecursionlimit(10000)
    dfs(1, *fixed[1])
    return found, nodes
 
 
if __name__ == "__main__":
    n, nodes = count(load_clues())
    print(f"ํƒ์ƒ‰ ๋…ธ๋“œ {nodes}๊ฐœ, ์™„์„ฑํ•ด {n}๊ฐœ")
    print("=> ํ•ด๊ฐ€ ์œ ์ผํ•˜๋‹ค" if n == 1 else "=> ํ•ด๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ๋‹ค(!)")
python3 count_solutions.py

count_solutions.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ํƒ์ƒ‰ ๋…ธ๋“œ 9948๊ฐœ๋ฅผ ์ „๋ถ€ ํ›‘์–ด ์™„์„ฑํ•ด๊ฐ€ ์ •ํ™•ํžˆ 1๊ฐœ์ž„์„ ํ™•์ธํ–ˆ๋‹ค
count_solutions.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ํƒ์ƒ‰ ๋…ธ๋“œ 9948๊ฐœ๋ฅผ ์ „๋ถ€ ํ›‘์–ด ์™„์„ฑํ•ด๊ฐ€ ์ •ํ™•ํžˆ 1๊ฐœ์ž„์„ ํ™•์ธํ–ˆ๋‹ค

๋…ธ๋“œ 9,948๊ฐœ๋ฅผ ์ „๋ถ€ ํ›‘์–ด ์™„์„ฑํ•ด๋Š” ํ•˜๋‚˜. ์ถœ์ œ์ž๊ฐ€ ํžŒํŠธ 150๊ฐœ๋ฅผ ์œ ์ผํ•ด๊ฐ€ ๋˜๋„๋ก ๊ณจ๋ผ ๋†“์€ ๊ฒƒ์ด๋‹ค.

์žฌํ˜„ ํ™˜๊ฒฝ๊ณผ ํ•œ ๋ฐฉ ์Šคํฌ๋ฆฝํŠธ

๋ฐฐํฌ๋ณธ ์••์ถ•์„ extracted/ ์— ํ’€์–ด ๋‘๊ณ  ์•„๋ž˜ ์Šคํฌ๋ฆฝํŠธ ํ•˜๋‚˜๋งŒ ๋Œ๋ฆฌ๋ฉด ์Šคํ… ์ œ๊ฑฐ๋ถ€ํ„ฐ flag ๋Œ€์กฐ๊นŒ์ง€ ๋๋‚œ๋‹ค. ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์•ˆ ์“ด๋‹ค.

#!/usr/bin/env bash
# Vernichtet (DreamHack Gold 4 / reversing) โ€” ํ•œ ๋ฐฉ ์žฌํ˜„.
#   anti-disassembly ์Šคํ… ์ œ๊ฑฐ -> ํžŒํŠธ ํ…Œ์ด๋ธ” ํŒŒ์‹ฑ -> Hidato ํ’€์ด -> ๋‹ต์•ˆ ํŒŒ์ผ -> flag ๋Œ€์กฐ
# ํ•„์š”ํ•œ ๊ฒƒ: python3, objdump(์„ ํƒ), ๋ฌธ์ œ ๋ฐฐํฌ๋ณธ extracted/main
set -eu
cd "$(dirname "$(readlink -f "$0")")"
 
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ œ.json'))['flag'])" 2>/dev/null || echo '')
 
[ -f extracted/main ] || { echo "โŒ extracted/main ์ด ์—†๋‹ค (challenge_2343.zip ์„ ํ’€์–ด๋ผ)"; exit 1; }
chmod +x extracted/main
 
# 1) ์•ˆํ‹ฐ๋””์Šค์–ด์…ˆ๋ธ”๋ฆฌ ์Šคํ… 97๊ฐœ๋ฅผ NOP ์œผ๋กœ ๋ฎ์€ ์‚ฌ๋ณธ (๋ถ„์„์šฉ, ํ’€์ด์—๋Š” ์—†์–ด๋„ ๋œ๋‹ค)
python3 deobf.py
 
# 2) ํžŒํŠธ ํ…Œ์ด๋ธ”(.data 0x4020) ํŒŒ์‹ฑ + Hidato ํ’€์ด + answer.bin ์ƒ์„ฑ
OUT=$(timeout 300 python3 solve.py 2>&1) || true
echo "$OUT" | tail -12
 
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
 
# 3) ๋ฌธ์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ์ง์ ‘ ์žฌํ™•์ธ โ€” Correct! ์™€ flag ๋ฅผ ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์Šค์Šค๋กœ ์ถœ๋ ฅํ•œ๋‹ค
echo
echo "--- ./extracted/main answer.bin ---"
BINOUT=$(./extracted/main answer.bin 2>&1 || true)
echo "$BINOUT"
BINFLAG=$(printf '%s' "$BINOUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
 
echo
if [ -z "$FLAG" ] || [ "$FLAG" != "$BINFLAG" ]; then
  echo "โŒ FAIL  solve.py='$FLAG' / binary='$BINFLAG'"; exit 1
fi
if [ -n "$EXPECT" ] && [ "$FLAG" != "$EXPECT" ]; then
  echo "โŒ FAIL  ๊ธฐ๋Œ€ '$EXPECT' / ์–ป์€ ๊ฐ’ '$FLAG'"; exit 1
fi
echo "โœ… PASS  $FLAG"

๋Œ๋ฆฐ ํ™˜๊ฒฝ์€ ์ด๋ ‡๋‹ค. ๊ธ€์— ๋‚˜์˜จ ์ฃผ์†Œยท๋ฐ”์ดํŠธ๋Š” ๋ฐฐํฌ๋ณธ ๊ณ ์œ ๊ฐ’์ด๋ผ ๋ฒ„์ „์ด ๋‹ฌ๋ผ๋„ ๊ทธ๋Œ€๋กœ์ง€๋งŒ, objdump ์ถœ๋ ฅ ํฌ๋งท๊ณผ r2 ์˜ afi ํ•„๋“œ ์ด๋ฆ„์€ ๋ฒ„์ „์„ ํƒ„๋‹ค.

ํ•ญ๋ชฉ๋ฒ„์ „
OS / ์ปค๋„Ubuntu 25.10 ยท 6.17.0-41-generic
Python3.13.7 (ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ)
objdumpGNU Binutils 2.45
gdb16.3
radare25.9.8


๐Ÿ”“ ๋ค โ€” ํŒŒ์ผ ์ด๋ฆ„์œผ๋กœ ๋ช…๋ น์ด ๋“ค์–ด๊ฐ„๋‹ค

ํ”Œ๋ž˜๊ทธ๋ฅผ ๋งŒ๋“œ๋Š” ๊ทธ sprintf ๋ฅผ ๋‹ค์‹œ ๋ณด์ž.

sprintf(cmd, "bash -c \"echo DH{$(sha256sum '%s' | awk '{print $1}')}\"", argv[1]);
system(cmd);

argv[1] ์ด ์ž‘์€๋”ฐ์˜ดํ‘œ ์•ˆ์— ๊ทธ๋Œ€๋กœ ๋“ค์–ด๊ฐ„๋‹ค. ์ด์Šค์ผ€์ดํ”„๊ฐ€ ์—†๋‹ค. ํŒŒ์ผ ์ด๋ฆ„์— ์ž‘์€๋”ฐ์˜ดํ‘œ๋ฅผ ๋„ฃ์–ด ๋”ฐ์˜ดํ‘œ๋ฅผ ๋‹ซ์•„ ๋ฒ„๋ฆฌ๋ฉด ๊ทธ ๋’ค๋Š” ์…ธ ๋ช…๋ น์ด ๋œ๋‹ค.

์ •๋‹ต ํŒŒ์ผ์„ ๊ทธ๋Ÿฐ ์ด๋ฆ„์œผ๋กœ ๋ณต์‚ฌํ•ด ๋‘๊ณ  ๋Œ๋ ค ๋ดค๋‹ค.

ls injtest/ && ./extracted/main "injtest/x'; id -un; echo '"

ํŒŒ์ผ ์ด๋ฆ„์— ์ž‘์€๋”ฐ์˜ดํ‘œ์™€ id ๋ช…๋ น์„ ๋„ฃ์–ด ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ โ€” ํ•ด์‹œ ๋Œ€์‹  ์‚ฌ์šฉ์ž ์ด๋ฆ„ jinho ๊ฐ€ ํ”Œ๋ž˜๊ทธ ์ž๋ฆฌ์— ๋“ค์–ด๊ฐ€ DH ์ค‘๊ด„ํ˜ธ jinho ๋กœ ์ถœ๋ ฅ๋œ๋‹ค
ํŒŒ์ผ ์ด๋ฆ„์— ์ž‘์€๋”ฐ์˜ดํ‘œ์™€ id ๋ช…๋ น์„ ๋„ฃ์–ด ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ โ€” ํ•ด์‹œ ๋Œ€์‹  ์‚ฌ์šฉ์ž ์ด๋ฆ„ jinho ๊ฐ€ ํ”Œ๋ž˜๊ทธ ์ž๋ฆฌ์— ๋“ค์–ด๊ฐ€ DH ์ค‘๊ด„ํ˜ธ jinho ๋กœ ์ถœ๋ ฅ๋œ๋‹ค

ํ”Œ๋ž˜๊ทธ ์ž๋ฆฌ์— jinho ๊ฐ€ ์ฐํ˜”๋‹ค. id -un ์ด ์‹ค์ œ๋กœ ๋Œ์•„๊ฐ„ ๊ฒƒ์ด๋‹ค.

์ด ๋ฌธ์ œ์—์„œ๋Š” ์•„๋ฌด ์ด๋“์ด ์—†๋‹ค. ์ฑ„์ ๋˜๋Š” ํ”Œ๋ž˜๊ทธ๋Š” ์ •๋‹ต ํŒŒ์ผ์˜ ํ•ด์‹œ๋กœ ์ด๋ฏธ ์ •ํ•ด์ ธ ์žˆ์œผ๋‹ˆ ๋ช…๋ น์„ ๋„ฃ์–ด ๋ด์•ผ ๋‹ค๋ฅธ ๊ฐ’์ด ๋‚˜์˜ฌ ๋ฟ์ด๋‹ค. ๋‹ค๋งŒ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ setuid ๋ฐ”์ด๋„ˆ๋ฆฌ๋‚˜ ์„œ๋ฒ„ ํ”„๋กœ์„ธ์Šค์˜€๋‹ค๋ฉด ์ด๊ฑด ๊ทธ๋Œ€๋กœ RCE ๋‹ค. sprintf ๋กœ ์…ธ ๋ช…๋ น์„ ์กฐ๋ฆฝํ•˜๊ณ  system ์— ๋„˜๊ธฐ๋Š” ํŒจํ„ด์€ CTF ๋ฐ–์—์„œ ๋งŒ๋‚˜๋ฉด ๋ฐ”๋กœ ์ทจ์•ฝ์ ์ด๋‹ค.


๐Ÿ“ ๊ฒฐ๋ก 

"ํŒŒ๊ดด๋๋‹ค"๋Š” ๋ง์€ ํŒŒ์ผ์ด ์•„๋‹ˆ๋ผ ๋„๊ตฌ๋ฅผ ๋‘๊ณ  ํ•œ ๋ง์ด์—ˆ๋‹ค

ELF ๋Š” ํ•œ ๊ตฐ๋ฐ๋„ ์†์ƒ๋˜์ง€ ์•Š์•˜๋‹ค. ์„น์…˜ ํ—ค๋”๋„, ํ”„๋กœ๊ทธ๋žจ ํ—ค๋”๋„, ์‹ฌ์ง€์–ด ์ฝ”๋“œ์˜ ์˜๋ฏธ์กฐ์ฐจ ๊ทธ๋Œ€๋กœ๋‹ค. ๋ถ€์„œ์ง„ ๊ฑด objdump ๊ฐ€ ํ™”๋ฉด์— ๊ทธ๋ ค ์ฃผ๋Š” ๊ทธ๋ฆผ๋ฟ์ด๋‹ค.

x86 ์˜ ๊ฐ€๋ณ€ ๊ธธ์ด ๋ช…๋ น์–ด๋Š” "๋ช…๋ น์˜ ์‹œ์ž‘"์ด๋ผ๋Š” ๊ฒŒ ๋ฐ”์ดํŠธ ์ž์ฒด์— ์•ˆ ์ ํ˜€ ์žˆ๋‹ค. ์–ด๋””์„œ๋ถ€ํ„ฐ ์ฝ๋А๋ƒ๋ฅผ ์ •ํ•˜๋Š” ๊ฑด ์ฝ๋Š” ์ชฝ์ด๊ณ , jmp $+1 ํ•˜๋‚˜๋ฉด ๊ทธ ํŒ๋‹จ์„ ๊ฐˆ๋ผ๋†“์„ ์ˆ˜ ์žˆ๋‹ค.

๋„๊ตฌ๊ฐ€ ํ‹€๋ฆฌ๋Š” ๋ฐฉ์‹์ด ๋„๊ตฌ๋งˆ๋‹ค ๋‹ค๋ฅด๋‹ค

objdump ๋Š” ์„ ํ˜•์ด๋ผ ์œ ๋ น ๋ช…๋ น์„ ๋ณด์—ฌ ์ฃผ๊ณ , radare2 ๋Š” ์žฌ๊ท€ ํ•˜๊ฐ•์ด๋ผ ๋ช…๋ น์€ ๋งž๊ฒŒ ์ฝ์ง€๋งŒ CFG ๊ฐ€ 25๊ฐœ ๋ธ”๋ก์œผ๋กœ ๋ถ€์„œ์ง„๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋„๋‹ฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ์ž๋ฆฌ์—์„œ๋Š” r2 ๋„ ์„ ํ˜•์œผ๋กœ ๋Œ์•„๊ฐ€ ๋˜‘๊ฐ™์ด ํ‹€๋ฆฐ๋‹ค.

๊ฐ™์€ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋‘ ๋„๊ตฌ์— ๋„ฃ์–ด ๋ณด๋Š” ๊ฒŒ ์™œ ์Šต๊ด€์ด ๋˜์–ด์•ผ ํ•˜๋Š”์ง€ ๋ณด์—ฌ ์ฃผ๋Š” ์˜ˆ๋‹ค. ๋‘˜์˜ ๊ฒฐ๊ณผ๊ฐ€ ์–ด๊ธ‹๋‚˜๋Š” ์ง€์ ์ด ๊ณง "์—ฌ๊ธฐ์„œ ๋ฌด์Šจ ์žฅ๋‚œ์„ ์ณค๋‹ค"๋Š” ํ‘œ์‹œ๋‹ค.

ํŒจ์น˜๋Š” ์ง€์šฐ์ง€ ๋ง๊ณ  ๋ฎ์–ด๋ผ

๋ฐ”์ดํŠธ๋ฅผ ์‚ญ์ œํ•˜๋ฉด ์ƒ๋Œ€ ๋ถ„๊ธฐ ๋ณ€์œ„๊ฐ€ ์ „๋ถ€ ์–ด๊ธ‹๋‚œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ทธ ์‹คํŒจ๊ฐ€ ์š”๋ž€ํ•˜๊ฒŒ ํ„ฐ์ง€์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒŒ ์ง„์งœ ํ•จ์ •์ด๋‹ค. ๋ช…๋ น์€ ์—ฌ์ „ํžˆ ๊ทธ๋Ÿด์‹ธํ•˜๊ฒŒ ๋””์ฝ”๋”ฉ๋˜๊ณ , lea ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ฃผ์†Œ๋งŒ 15๋ฐ”์ดํŠธ์”ฉ ๋ฐ€๋ฆฐ๋‹ค. ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ”์„ ์—‰๋šฑํ•œ ๋ฐ์„œ ์ฝ์œผ๋ฉด์„œ "๊ฐ’์ด ์ด์ƒํ•œ๋ฐ" ํ•˜๊ณ  ๋ช‡ ์‹œ๊ฐ„์„ ํƒœ์šธ ์ˆ˜ ์žˆ๋‹ค. ๊ธธ์ด๋ฅผ ์œ ์ง€ํ•˜๋Š” NOP ๋ฎ์–ด์“ฐ๊ธฐ๋Š” ๊ทธ๋ž˜์„œ ์•ˆ์ „ํ•˜๋‹ค.

๋ฐฉ์–ด ๊ด€์ ์—์„œ

์•ˆํ‹ฐ ๋””์Šค์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ๋ถ„์„์„ ๋Šฆ์ถœ ๋ฟ ๋ง‰์ง€๋Š” ๋ชปํ•œ๋‹ค. ์—ฌ๊ธฐ ์“ฐ์ธ ์Šคํ…์€ ํŒจํ„ด์ด ์™„์ „ํžˆ ๊ณ ์ •๋ผ ์žˆ์–ด์„œ 5์ค„์งœ๋ฆฌ bytes.replace ํ•˜๋‚˜๋กœ ์ „๋ถ€ ๊ฑทํžŒ๋‹ค. ์ƒ์šฉ ๋ณดํ˜ธ์—์„œ ์ด๋Ÿฐ ๊ธฐ๋ฒ•์„ ์“ด๋‹ค๋ฉด ์ตœ์†Œํ•œ ํŒจํ„ด์„ ๋งค๋ฒˆ ๋‹ค๋ฅด๊ฒŒ ์ƒ์„ฑํ•ด์•ผ ์˜๋ฏธ๊ฐ€ ์žˆ๊ณ , ๊ทธ๋ž˜๋„ ๋™์  ๋ถ„์„ ์•ž์—์„œ๋Š” stepi ๋ช‡ ๋ฒˆ์ด๋ฉด ๋“œ๋Ÿฌ๋‚œ๋‹ค.

์ •์ž‘ ์ด ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ์‹ค์ œ ๋ณด์•ˆ ๋ฌธ์ œ๊ฐ€ ๋  ๋งŒํ•œ ๊ณณ์€ ๋‚œ๋…ํ™”๊ฐ€ ์•„๋‹ˆ๋ผ ๋งˆ์ง€๋ง‰์˜ sprintf + system ์ด์—ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ์…ธ ๋ฌธ์ž์—ด์— ๋ผ์›Œ ๋„ฃ์ง€ ๋ง๊ณ , ๊ผญ ์™ธ๋ถ€ ๋ช…๋ น์„ ์จ์•ผ ํ•˜๋ฉด execve ๊ณ„์—ด๋กœ ์ธ์ž๋ฅผ ๋ฐฐ์—ด์งธ ๋„˜๊ธฐ๋Š” ๊ฒŒ ๋งž๋‹ค.

์ด ๊ธ€์ด ๋„์›€์ด ๋๋‚˜์š”?

Comments

๋Œ“๊ธ€

0๊ฐœ

๋Œ“๊ธ€์„ ๋‚จ๊ธฐ๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ด์š”. (๋„ค์ด๋ฒ„ ยท ๊ตฌ๊ธ€ ๊ณ„์ •)

๋Œ“๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘โ€ฆ

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅ‡ Gold 1] ํ•จ์ˆ˜ 125๊ฐœ์งœ๋ฆฌ 4์ง„ ํŠธ๋ผ์ด๋ฅผ ๋˜๊ฐ๋‹ค โ€” DreamHack Function Network ํ’€์ด
blog

[๐Ÿฅ‡ Gold 1] ํ•จ์ˆ˜ 125๊ฐœ์งœ๋ฆฌ 4์ง„ ํŠธ๋ผ์ด๋ฅผ ๋˜๊ฐ๋‹ค โ€” DreamHack Function Network ํ’€์ด

stripped ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ 64๋ฐ”์ดํŠธ ์ž…๋ ฅ์„ 10000๋ฒˆ ๋ณ€ํ˜•ํ•ด ๊ณ ์ •๋œ ๋ชฉํ‘œ๊ฐ’๊ณผ ๋น„๊ตํ•œ๋‹ค. ๋ณ€ํ˜•์˜ ์ข…๋ฅ˜๋Š” .data ์— ๋ฐ•ํžŒ 64๋น„ํŠธ key ๊ฐ€ ์ •ํ•˜๋Š”๋ฐ, ๊ทธ key ๋Š” ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ ๋””์ŠคํŒจ์ฒ˜ 125๊ฐœ๋กœ ์งœ์ธ 4์ง„ ํŠธ๋ผ์ด๋ฅผ 2๋น„ํŠธ์”ฉ 32๋ฒˆ ํƒ€๊ณ  ๋‚ด๋ ค๊ฐ€๋Š” ๊ฒฝ๋กœ์˜€๋‹ค. ๋„์ฐฉ์ง€์ธ ๋ฆฌํ”„ 4๊ฐœ๊ฐ€ ์ „๋ถ€ ์ „๋‹จ์‚ฌ๋ผ ๋ชฉํ‘œ๊ฐ’์—์„œ 10000์Šคํ…์„ ๊ฑฐ๊พธ๋กœ ๊ฐ์œผ๋ฉด ์ž…๋ ฅ์ด ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค.
#dreamhack#ctf#reversing+6
2026-08-16#dreamhack +5
[๐Ÿฅ‡ Gold 4] ํ•˜๋ฃจ ๋„˜๊ฒŒ ๊ฑธ๋ฆฌ๋Š” ์ •๋ ฌ์„ 0.2์ดˆ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ โ€” DreamHack Slooooow ํ’€์ด
blog

[๐Ÿฅ‡ Gold 4] ํ•˜๋ฃจ ๋„˜๊ฒŒ ๊ฑธ๋ฆฌ๋Š” ์ •๋ ฌ์„ 0.2์ดˆ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ โ€” DreamHack Slooooow ํ’€์ด

๋ฐฐํฌ๋ณธ์€ stripped PIE ํ•˜๋‚˜๋ฟ์ด๊ณ  ์ฝ”๋“œ๋Š” 1,136๋ฐ”์ดํŠธ, ๋ฐ์ดํ„ฐ๋Š” 400KB๋‹ค. main ์ด ํ•˜๋Š” ์ผ์€ ๊ทธ 400KB๋ฅผ Stooge sort ๋กœ ์ •๋ ฌํ•˜๊ณ  SHA-256 ์„ ์ฐ๋Š” ๊ฒƒ๋ฟ์ธ๋ฐ, ๋งˆ์Šคํ„ฐ ์ •๋ฆฌ๋ฅผ ์„ธ์›Œ ๋ณด๋ฉด 10๋งŒ ์›์†Œ์— ํ•˜๋ฃจ๊ฐ€ ๋„˜๊ฒŒ ๊ฑธ๋ฆฐ๋‹ค. ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‹คํ–‰ํ•  ํ•„์š”๋Š” ์—†๋‹ค. ๋น„๊ต ํ•จ์ˆ˜๊ฐ€ ์“ฐ๋Š” ๋น„ํŠธ ์ˆœ์—ด๋งŒ ๊ทธ๋Œ€๋กœ ๋ณต์›ํ•˜๋ฉด ๊ฒฐ๊ณผ๋Š” ํŒŒ์ด์ฌ ํ•œ ์ค„๋กœ ๋‚˜์˜จ๋‹ค.
#dreamhack#ctf#reversing+7
2026-08-10#dreamhack +5
[๐Ÿฅ‡ Gold 4] ์•ˆํ‹ฐ๋””๋ฒ„๊น…์ด ์Šค์Šค๋กœ ๋ฌด๋„ˆ์ง€๋Š” ์ž๋ฆฌ โ€” DreamHack ptrace_block ํ’€์ด
blog

[๐Ÿฅ‡ Gold 4] ์•ˆํ‹ฐ๋””๋ฒ„๊น…์ด ์Šค์Šค๋กœ ๋ฌด๋„ˆ์ง€๋Š” ์ž๋ฆฌ โ€” DreamHack ptrace_block ํ’€์ด

์ƒ์„ฑ์ž๊ฐ€ AES-128 ํ‚ค๋ฅผ ๋งŒ๋“ค๊ณ  main ์€ ๊ทธ ํ‚ค๋กœ ์ž…๋ ฅ์„ ์•”ํ˜ธํ™”ํ•ด out.txt ์— ์“ด๋‹ค. ํ‚ค๋ฅผ ํ”๋“œ๋Š” ๊ฐ’์€ ptrace(PTRACE_TRACEME) ์˜ ๋ฐ˜ํ™˜๊ฐ’์„ 4096 ๋ฒˆ ๊ณฑํ•ด ๋งŒ๋“œ๋Š”๋ฐ, 32๋น„ํŠธ ๊ณฑ์…ˆ์ด ๋„˜์ณ ๋””๋ฒ„๊ฑฐ๊ฐ€ ์žˆ๋“  ์—†๋“  0 ์ด ๋œ๋‹ค. ๋‚จ์€ ๊ฑด ํ•˜์œ„ 1๋ฐ”์ดํŠธ์งœ๋ฆฌ XOR ๋ฟ์ด๋ผ 256 ๋ฒˆ ์‹œ๋„๋กœ ๋๋‚œ๋‹ค.
#dreamhack#ctf#reversing+6
2026-08-07#dreamhack +5