[๐Ÿฅˆ Silver 3] 2MB ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ์ฝ”๋“œ๋Š” 509๋ฐ”์ดํŠธ๋ฟ์ด์—ˆ๋‹ค โ€” DreamHack Branches and Leaves ํ’€์ด

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

[๐Ÿฅˆ Silver 3] 2MB ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ์ฝ”๋“œ๋Š” 509๋ฐ”์ดํŠธ๋ฟ์ด์—ˆ๋‹ค โ€” DreamHack Branches and Leaves ํ’€์ด

๋ฌธ์ œ ์„ค๋ช…์ด ์‹œ ๋„ค ์ค„๋ฟ์ธ ๋ฆฌ๋ฒ„์‹ฑ. 2.1MB์งœ๋ฆฌ stripped ELF๋ฅผ ์—ด์–ด ๋ณด๋ฉด ์ฝ”๋“œ๋Š” 509๋ฐ”์ดํŠธ๊ณ  ๋‚˜๋จธ์ง€ ์ „๋ถ€๊ฐ€ ๋ฐฐ์—ด๋กœ ์ ‘์€ ๊นŠ์ด 16 ์ด์ง„ํŠธ๋ฆฌ๋‹ค. ์ž…๋ ฅ 4๊ธ€์ž๊ฐ€ 16๋น„ํŠธ๋กœ ํŽด์ ธ ํŠธ๋ฆฌ๋ฅผ 16๋ฒˆ ๋‚ด๋ ค๊ฐ€๊ณ , ๊ทธ ๋ฆฌํ”„ 65536๊ฐœ๊ฐ€ ์„œ๋กœ ๊ฒน์น˜์ง€ ์•Š์•„์„œ ๊ธฐ๋Œ€๊ฐ’๋งˆ๋‹ค ์—ญ์ƒ์ด ์ •ํ™•ํžˆ ํ•˜๋‚˜๋กœ ์ •ํ•ด์ง„๋‹ค.

๋ฌธ์ œ: DreamHack โ€” Branches and Leaves ๋ถ„๋ฅ˜: reversing ๋‚œ์ด๋„: ๐Ÿฅˆ Silver 3 FLAG: DH{d8f794325872bab95cfaa117545c6c0b77059a74f4dd31d8c2bf130a59745d77}

๋ฌธ์ œ ์„ค๋ช…์— ํžŒํŠธ๊ฐ€ ์—†๋‹ค. ์‹œ ๋„ค ์ค„์ด ์ „๋ถ€๋‹ค.

Branches in twilight, a silhouette's embrace, Leaves in the moonlight, a shimmering grace.

๊ฐ€์ง€์™€ ์žŽ์‚ฌ๊ท€. ๋‚˜์ค‘์— ๋ณด๋‹ˆ ์ด๊ฒŒ ํžŒํŠธ์ด๊ธด ํ–ˆ๋Š”๋ฐ, ์ฒ˜์Œ ๋ฐ›์•˜์„ ๋• ๊ทธ๋ƒฅ ๋ถ„์œ„๊ธฐ ๊ธ€์ธ ์ค„ ์•Œ์•˜๋‹ค.

๋ฐ›์€ ํŒŒ์ผ์€ main ํ•˜๋‚˜. 2,111,704๋ฐ”์ดํŠธ์งœ๋ฆฌ stripped PIE ELF๋‹ค. ์ด ํฌ๊ธฐ๋ฉด ๋ณดํ†ต Go๋‚˜ Rust๋กœ ์ •์  ๋งํฌํ•œ ๋ฌผ๊ฑด์„ ์˜์‹ฌํ•˜๋Š”๋ฐ, ์‹ฌ๋ณผ ํ…Œ์ด๋ธ”์„ ์—ด๋ฉด ๊ทธ๊ฒŒ ์•„๋‹ˆ๋ผ๋Š” ๊ฒŒ ๋ฐ”๋กœ ๋“œ๋Ÿฌ๋‚œ๋‹ค.

file extracted/main; readelf -W --dyn-syms extracted/main

file ๊ณผ readelf --dyn-syms ์ถœ๋ ฅ โ€” 2.1MB stripped PIE ์ธ๋ฐ ์ž„ํฌํŠธ๋Š” strlen ๊ณผ printf ๊ณ„์—ด, exit ์„ ํฌํ•จํ•ด ๋‹ค์„ฏ ๊ฐœ๋ฟ์ด๋‹ค
file ๊ณผ readelf --dyn-syms ์ถœ๋ ฅ โ€” 2.1MB stripped PIE ์ธ๋ฐ ์ž„ํฌํŠธ๋Š” strlen ๊ณผ printf ๊ณ„์—ด, exit ์„ ํฌํ•จํ•ด ๋‹ค์„ฏ ๊ฐœ๋ฟ์ด๋‹ค

์ž„ํฌํŠธ๊ฐ€ strlen, __printf_chk, exit ๊ทธ๋ฆฌ๊ณ  ์‹œ์ž‘ยท์ข…๋ฃŒ ํ›…๊นŒ์ง€ ๋‹ค์„ฏ ๊ฐœ๋‹ค. 2MB๋ฅผ ์ฐจ์ง€ํ•˜๋Š” ๊ฒŒ ์ฝ”๋“œ์ผ ๋ฆฌ๊ฐ€ ์—†๋‹ค.


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…Branches and Leaves
๋‚œ์ด๋„๐Ÿฅˆ Silver 3
๋ถ„๋ฅ˜reversing
์ œ๊ณต ํŒŒ์ผmain (2,111,704B, stripped PIE, x86-64)
์„œ๋ฒ„์—†์Œ โ€” ํŒŒ์ผ๋งŒ ์ค€๋‹ค
ํ•ต์‹ฌ ๊ธฐ๋ฒ•๋ฐฐ์—ด๋กœ ์ ‘์€ ๊นŠ์ด 16 ์ด์ง„ํŠธ๋ฆฌ + ๋ฆฌํ”„๊ฐ€ ์ „๋‹จ์‚ฌ๋ผ ์—ญ์ƒ์ด ์œ ์ผ

ํ”„๋กœ๊ทธ๋žจ์€ ์ธ์ž ํ•˜๋‚˜๋ฅผ ๋ฐ›์•„ ๊ฒ€์ฆํ•˜๊ณ , ํ†ต๊ณผํ•˜๋ฉด ๊ทธ ์ธ์ž๋ฅผ ๊ทธ๋Œ€๋กœ ํ”Œ๋ž˜๊ทธ ์„œ์‹์— ๋ผ์›Œ ์ถœ๋ ฅํ•œ๋‹ค. ์ฆ‰ ์ž…๋ ฅ์ด ๊ณง ํ”Œ๋ž˜๊ทธ๋‹ค. 64๊ธ€์ž๋ฅผ ๋งžํžˆ๋ฉด ๋์ด๋‹ค.



๐Ÿ”ฌ ์ •์ฐฐ โ€” ์ฝ”๋“œ๊ฐ€ 509๋ฐ”์ดํŠธ๋‹ค

์„น์…˜ ํฌ๊ธฐ๋ถ€ํ„ฐ ๋ณธ๋‹ค. ํŒŒ์ผ์ด 2MB์ธ๋ฐ ์–ด๋””๊ฐ€ 2MB์ธ์ง€ ์•Œ์•„์•ผ ํ•œ๋‹ค.

ls -l extracted/main; readelf -S -W extracted/main | grep -E "\[Nr\]|\.text|\.rodata|\.data|\.bss"

readelf -S ๋กœ ๋ณธ ์„น์…˜ ํฌ๊ธฐ โ€” ์ฝ”๋“œ์ธ .text ๋Š” 0x1fd ์ฆ‰ 509๋ฐ”์ดํŠธ๋ฟ์ด๊ณ  .data ๊ฐ€ 0x200060 ์œผ๋กœ ์•ฝ 2MB ๋ฅผ ํ†ต์งธ๋กœ ์ฐจ์ง€ํ•œ๋‹ค
readelf -S ๋กœ ๋ณธ ์„น์…˜ ํฌ๊ธฐ โ€” ์ฝ”๋“œ์ธ .text ๋Š” 0x1fd ์ฆ‰ 509๋ฐ”์ดํŠธ๋ฟ์ด๊ณ  .data ๊ฐ€ 0x200060 ์œผ๋กœ ์•ฝ 2MB ๋ฅผ ํ†ต์งธ๋กœ ์ฐจ์ง€ํ•œ๋‹ค

.text ๊ฐ€ 0x1fd, 509๋ฐ”์ดํŠธ๋‹ค. .rodata ๋Š” 36๋ฐ”์ดํŠธ. ๊ทธ๋ฆฌ๊ณ  .data ๊ฐ€ 0x200060, 2,097,248๋ฐ”์ดํŠธ.

ํŒŒ์ผ์˜ 99% ์ด์ƒ์ด ๋ฐ์ดํ„ฐ๋‹ค. ๋กœ์ง์€ 500๋ฐ”์ดํŠธ ์•ˆ์— ๋‹ค ๋“ค์–ด ์žˆ๊ณ  ๋‚˜๋จธ์ง€๋Š” ์ „๋ถ€ ๊ทธ ๋กœ์ง์ด ์ฝ์„ ํ‘œ๋ผ๋Š” ๋œป์ด๋‹ค. ์ด ์‹œ์ ์—์„œ ๋””์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ๋ถ™์ผ ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ๋„ ์ •ํ•ด์ง„๋‹ค โ€” 509๋ฐ”์ดํŠธ๋ฉด ์†์œผ๋กœ ์ฝ๋Š” ๊ฒŒ ๋น ๋ฅด๋‹ค.

๋ฌธ์ž์—ด๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์งง๋‹ค.

objdump -s -j .rodata extracted/main; strings -a -n 8 extracted/main | grep -aE "use|DH"

objdump -s -j .rodata ์ถœ๋ ฅ โ€” ์‚ฌ์šฉ๋ฒ• ์•ˆ๋‚ด ํ•œ ์ค„๊ณผ ๊ฒ€์ฆ ํ†ต๊ณผ ์‹œ ์ž…๋ ฅ์„ ๊ทธ๋Œ€๋กœ ๋ผ์›Œ ๋„ฃ๋Š” ํ”Œ๋ž˜๊ทธ ์„œ์‹ ํ•˜๋‚˜๊ฐ€ ์ „๋ถ€๋‹ค
objdump -s -j .rodata ์ถœ๋ ฅ โ€” ์‚ฌ์šฉ๋ฒ• ์•ˆ๋‚ด ํ•œ ์ค„๊ณผ ๊ฒ€์ฆ ํ†ต๊ณผ ์‹œ ์ž…๋ ฅ์„ ๊ทธ๋Œ€๋กœ ๋ผ์›Œ ๋„ฃ๋Š” ํ”Œ๋ž˜๊ทธ ์„œ์‹ ํ•˜๋‚˜๊ฐ€ ์ „๋ถ€๋‹ค

How to use: %s <input> ๊ณผ ํ”Œ๋ž˜๊ทธ ์„œ์‹ ํ•˜๋‚˜. ์„œ์‹์ด ์ž…๋ ฅ์„ ๊ทธ๋Œ€๋กœ ๋ฐ›์•„ ์ฐ๋Š” ํ˜•ํƒœ๋ผ, ํ”Œ๋ž˜๊ทธ๊ฐ€ ํŒŒ์ผ ์–ด๋”˜๊ฐ€์— ์•”ํ˜ธํ™”๋ผ ์ˆจ์–ด ์žˆ๋Š” ๊ฒŒ ์•„๋‹ˆ๋‹ค. ๊ฒ€์ฆ์„ ํ†ต๊ณผํ•˜๋Š” ์ž…๋ ฅ ์ž์ฒด๊ฐ€ ๋‹ต์ด๋‹ค.


๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” ๋ฐฐ์—ด๋กœ ์ ‘์€ ์ด์ง„ํŠธ๋ฆฌ

ํŠธ๋ฆฌ๋ฅผ ํฌ์ธํ„ฐ๋กœ ๋งŒ๋“ค๋ฉด ๋…ธ๋“œ๋งˆ๋‹ค ์ฃผ์†Œ๊ฐ€ ํ•„์š”ํ•˜๊ณ , ๊ทธ ์ฃผ์†Œ๋Š” PIE์—์„œ ์žฌ๋ฐฐ์น˜ ๋Œ€์ƒ์ด ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ํ‘œ๋กœ ๊ตณํ˜€ ๋„ฃ์„ ๋• ๋ณดํ†ต ๋ฐฐ์—ด๋กœ ์ ‘๋Š”๋‹ค.

๋…ธ๋“œ์— ๋ฒˆํ˜ธ๋ฅผ ๋งค๊ธฐ๊ณ  ์ž์‹ = ํ‘œ[2 * ๋…ธ๋“œ๋ฒˆํ˜ธ + ๋ฐฉํ–ฅ] ์œผ๋กœ ์ƒ‰์ธํ•˜๋ฉด ํฌ์ธํ„ฐ๊ฐ€ ํ•„์š” ์—†๋‹ค. ํž™(heap)์„ ๋ฐฐ์—ด๋กœ ๋‹ค๋ฃจ๋Š” ๋ฐฉ์‹๊ณผ ๊ฐ™์€ ์ฐฉ์ƒ์ธ๋ฐ, ๋‹ค๋ฅธ ์ ์€ ๋ฒˆํ˜ธ๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ์•ˆ ์ค˜๋„ ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์•„๋ฌด ๋ฒˆํ˜ธ๋‚˜ ์ฃผ๊ณ  ํ‘œ๋ฅผ ๊ทธ๋งŒํผ ํฌ๊ฒŒ ์žก์œผ๋ฉด ๊ทธ๋งŒ์ด๋‹ค.

์ด ๋ฌธ์ œ์˜ ํ‘œ๋Š” ๊ทธ๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด์กŒ๋‹ค. ๋’ค์—์„œ ์„ธ์–ด ๋ณด๊ฒ ์ง€๋งŒ, ์‹ค์ œ๋กœ ์“ฐ๋Š” ๋…ธ๋“œ๋Š” 65,535๊ฐœ์ธ๋ฐ ๋ฒˆํ˜ธ๋Š” 262,139๋ฒˆ๊นŒ์ง€ ํฉ์–ด์ ธ ์žˆ๋‹ค. ๊ทธ ์ฐจ์ด๊ฐ€ ๊ณง ํŒŒ์ผ ํฌ๊ธฐ๋‹ค.



๐Ÿ’ฃ ๊ฒ€์ฆ ํ•จ์ˆ˜ โ€” ๋ฃจํ”„ ๋‘ ๊ฐœ๊ฐ€ ์ „๋ถ€

main ์€ ์ธ์ž ๊ฐœ์ˆ˜๋ฅผ ์„ธ๊ณ  0x11e0 ์˜ ํ•จ์ˆ˜๋ฅผ ๋ถ€๋ฅธ ๋’ค ์„œ์‹์„ ์ถœ๋ ฅํ•˜๋Š” ๊ฒŒ ์ „๋ถ€๋‹ค. ํŒ์ •์€ ์ „๋ถ€ ๊ทธ ํ•จ์ˆ˜ ์•ˆ์— ์žˆ๋‹ค.

์ฝ๊ธฐ ํŽธํ•˜๊ฒŒ objdump ์ถœ๋ ฅ์— ์ฃผ์„์„ ์–น์—ˆ๋‹ค. ์น˜ํ™˜ ๊ทœ์น™์€ ์ž‘์—… ํด๋”์˜ annot.sed ์— ๊ทธ๋Œ€๋กœ ์žˆ๋‹ค.

s|\(cmp    rax,0x40\)|\1                 <-- ์ž…๋ ฅ์€ ์ •ํ™•ํžˆ 64๊ธ€์ž|
s|\(lea    r10,\[rip+0x2e14\]\).*|\1        <-- 0x4020  expected[16]|
s|\(lea    rdi,\[rip+0x2e5b\]\).*|\1        <-- 0x4060  tree[524288]|
s|\(shl    edx,0x4\)|\1                   <-- ๋‹ˆ๋ธ”์„ ์œ„์—์„œ๋ถ€ํ„ฐ ์Œ“๋Š”๋‹ค|
s|\(cmp    sil,0x9\)|\1                   <-- '0'-'9' ์ธ๊ฐ€|
s|\(cmp    sil,0x5\)|\1                   <-- 'a'-'f' ์ธ๊ฐ€ (๋Œ€๋ฌธ์ž ๋ถˆ๊ฐ€)|
s|\(mov    ecx,0x10\)|\1                  <-- ํŠธ๋ฆฌ๋ฅผ 16๋ฒˆ ๋‚ด๋ ค๊ฐ„๋‹ค|
s|\(sar    edx,1\)|\1                     <-- ๋‹ค์Œ ๋น„ํŠธ๋กœ (LSB ๋ถ€ํ„ฐ)|
s|\(and    esi,0x1\)|\1                   <-- ์ด๋ฒˆ ๋น„ํŠธ|
s|\(lea    rax,\[rsi+rax\*2\]\)|\1          <-- ์ž์‹ ์œ„์น˜ = 2*node + bit|
s|\(movsxd rax,DWORD PTR \[rdi+rax\*4\]\)|\1  <-- node = tree[...]|
s|\(cmp    eax,0xffffffff\)|\1            <-- -1 ์€ ๋ง‰๋‹ค๋ฅธ ๊ฐ€์ง€|
s|\(cmp    eax,0x3ffff\)|\1               <-- ๋‚ด๋ถ€๋…ธ๋“œ ์ƒํ•œ 262143|
s|\(cmp    eax,DWORD PTR \[r10+rdx\*4\]\)|\1  <-- 16์Šคํ… ๋’ค ๊ฐ’ == expected[i] ?|
s|\(cmp    r9d,0x40\)|\1                  <-- 16๋ฉ์–ด๋ฆฌ๋ฅผ ๋‹ค ๋ดค๋‚˜|

์•ž์ชฝ ์ ˆ๋ฐ˜๋ถ€ํ„ฐ.

objdump -d --start-address=0x11e0 --stop-address=0x1240 -M intel extracted/main | sed -f annot.sed

objdump ๋กœ ์ž๋ฅธ ๊ฒ€์ฆ ํ•จ์ˆ˜ ์•ž๋ถ€๋ถ„ โ€” strlen ๊ฒฐ๊ณผ๋ฅผ 0x40 ๊ณผ ๋น„๊ตํ•ด 64๊ธ€์ž๋ฅผ ๊ฐ•์ œํ•˜๊ณ  ๋„ค ๊ธ€์ž๋ฅผ 16์ง„์ˆ˜๋กœ ์ ‘์–ด edx ์— ์Œ“๋Š”๋‹ค
objdump ๋กœ ์ž๋ฅธ ๊ฒ€์ฆ ํ•จ์ˆ˜ ์•ž๋ถ€๋ถ„ โ€” strlen ๊ฒฐ๊ณผ๋ฅผ 0x40 ๊ณผ ๋น„๊ตํ•ด 64๊ธ€์ž๋ฅผ ๊ฐ•์ œํ•˜๊ณ  ๋„ค ๊ธ€์ž๋ฅผ 16์ง„์ˆ˜๋กœ ์ ‘์–ด edx ์— ์Œ“๋Š”๋‹ค

์ฒซ ๊ด€๋ฌธ์€ ๊ธธ์ด๋‹ค. strlen ๊ฒฐ๊ณผ๋ฅผ 0x40 ๊ณผ ๋น„๊ตํ•˜๋‹ˆ ์ •ํ™•ํžˆ 64๊ธ€์ž.

๊ทธ๋‹ค์Œ r8 ์„ rbx+4 ๋กœ ์žก๊ณ  4๊ธ€์ž์”ฉ ๋Š๋Š”๋‹ค. shl edx,0x4 ๋‹ค์Œ์— add edx,esi โ€” ์ „ํ˜•์ ์ธ 16์ง„์ˆ˜ ํŒŒ์„œ๋‹ค. ์ˆซ์ž๋Š” -0x30, ๊ทธ๊ฒŒ ์•„๋‹ˆ๋ฉด 0x1280 ์œผ๋กœ ํŠ€์–ด์„œ -0x57 ์„ ๋บ€๋‹ค. ๋Œ€๋ฌธ์ž ๋ถ„๊ธฐ๋Š” ์•„์˜ˆ ์—†๋‹ค. 'A' - 'a' ๋Š” ์Œ์ˆ˜๊ณ  ๊ทธ ๋น„๊ต๊ฐ€ unsigned(ja)๋ผ ๊ทธ๋Œ€๋กœ ํŠ•๊ธด๋‹ค.

C๋กœ ์˜ฎ๊ธฐ๋ฉด ์ด๋ ‡๋‹ค.

unsigned edx = 0;
for (char *c = chunk; c < chunk + 4; c++) {
    edx <<= 4;
    if      ((unsigned char)(*c - '0') <= 9) edx += *c - '0';
    else if ((unsigned char)(*c - 'a') <= 5) edx += *c - 'a' + 10;
    else exit(1);
}

์ž…๋ ฅ์€ ์†Œ๋ฌธ์ž 16์ง„์ˆ˜ 64๊ธ€์ž์ด๊ณ , 4๊ธ€์ž๋งˆ๋‹ค 16๋น„ํŠธ ์ •์ˆ˜๊ฐ€ ํ•˜๋‚˜์”ฉ ๋‚˜์˜จ๋‹ค. ์ด 16๊ฐœ.

์ด์ œ ๋’ค์ชฝ ์ ˆ๋ฐ˜.

objdump -d --start-address=0x1240 --stop-address=0x129d -M intel extracted/main | sed -f annot.sed

objdump ๋กœ ์ž๋ฅธ ๊ฒ€์ฆ ํ•จ์ˆ˜ ๋’ท๋ถ€๋ถ„ โ€” ๋น„ํŠธ๋ฅผ ํ•˜๋‚˜์”ฉ ๊บผ๋‚ด ํ‘œ๋ฅผ ์ฝ๋Š” 5์ค„ ๋ฃจํ”„๋ฅผ 16๋ฒˆ ๋Œ๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ expected ์™€ ๋น„๊ตํ•œ๋‹ค
objdump ๋กœ ์ž๋ฅธ ๊ฒ€์ฆ ํ•จ์ˆ˜ ๋’ท๋ถ€๋ถ„ โ€” ๋น„ํŠธ๋ฅผ ํ•˜๋‚˜์”ฉ ๊บผ๋‚ด ํ‘œ๋ฅผ ์ฝ๋Š” 5์ค„ ๋ฃจํ”„๋ฅผ 16๋ฒˆ ๋Œ๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ expected ์™€ ๋น„๊ตํ•œ๋‹ค

mov ecx,0x10 ์ด ๋ฐ˜๋ณต ํšŸ์ˆ˜ 16์ด๋‹ค. ๋ณธ์ฒด๋Š” ๋‹ค์„ฏ ์ค„๋ฟ์ด๋‹ค.

mov    esi,edx              ; esi = ์ง€๊ธˆ ๊ฐ’
sar    edx,1                ; ๊ฐ’์€ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ํ•œ ์นธ
and    esi,0x1              ; ๋ฐฉ๊ธˆ ๋ฐ€๋ ค๋‚œ ์ž๋ฆฌ์˜ ๋น„ํŠธ
lea    rax,[rsi+rax*2]      ; ์ƒ‰์ธ = 2 * node + bit
movsxd rax,[rdi+rax*4]      ; node = tree[์ƒ‰์ธ]

rdi ๋Š” .data+0x60(ํ‘œ), r10 ์€ .data+0x20(๊ธฐ๋Œ€๊ฐ’)์ด๋‹ค. ๋น„ํŠธ๋ฅผ LSB๋ถ€ํ„ฐ ํ•˜๋‚˜์”ฉ ๊บผ๋‚ด ํ‘œ๋ฅผ ๋”ฐ๋ผ ๋‚ด๋ ค๊ฐ„๋‹ค. ์•ž์˜ ๋ฐฐ๊ฒฝ ๊ทธ๋Œ€๋กœ๋‹ค.

๋ฃจํ”„ ์•ž๋จธ๋ฆฌ์˜ ๋‘ ๊ฒ€์‚ฌ๊ฐ€ ๊ฒฝ๊ณ„๋‹ค. ๊ฐ’์ด -1 ์ด๋ฉด ์ฃฝ๊ณ , 0x3ffff ๋ฅผ ๋„˜์–ด๋„ ์ฃฝ๋Š”๋‹ค. ์ฆ‰ ๋‚ด๋ถ€ ๋…ธ๋“œ ๋ฒˆํ˜ธ์˜ ์ƒํ•œ์ด 262,143์ด๋‹ค. ์ด ๊ฒ€์‚ฌ๋Š” ๋งค ๋ฐ˜๋ณต์˜ ์‹œ์ž‘์— ์žˆ์œผ๋ฏ€๋กœ ๋งˆ์ง€๋ง‰ 16๋ฒˆ์งธ๋กœ ์ฝ์€ ๊ฐ’์—๋Š” ์•ˆ ๊ฑธ๋ฆฐ๋‹ค. ๊ทธ ๊ฐ’์ด ๊ณง ๋ฆฌํ”„๋‹ค.

16์Šคํ…์ด ๋๋‚˜๋ฉด cmp eax,[r10+rdx*4] ๋กœ r9d/4 ๋ฒˆ์งธ ๊ธฐ๋Œ€๊ฐ’๊ณผ ๋งž์ถ˜๋‹ค. 16๋ฉ์–ด๋ฆฌ๋ฅผ ์ „๋ถ€ ํ†ต๊ณผํ•ด์•ผ ret ์ด๋‹ค.

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

for (int i = 0; i < 16; i++) {
    unsigned v = parse4(input + i * 4);
    int node = 0;
    for (int s = 0; s < 16; s++) {
        if (s && (node == -1 || node > 0x3ffff)) exit(1);
        node = tree[2 * node + (v & 1)];
        v >>= 1;
    }
    if (node != expected[i]) exit(1);
}

์—ฌ๊ธฐ์„œ ๊ฒฐ์ •์ ์ธ ๊ฒŒ ํ•˜๋‚˜ ์žˆ๋‹ค. ๋ฉ์–ด๋ฆฌ 16๊ฐœ๊ฐ€ ์„œ๋กœ ์•„๋ฌด ์ƒ๊ด€์ด ์—†๋‹ค. ์•ž ๋ฉ์–ด๋ฆฌ์˜ ์ƒํƒœ๊ฐ€ ๋’ค๋กœ ์ด์–ด์ง€์ง€ ์•Š๊ณ , ๋งค๋ฒˆ node = 0 ์—์„œ ๋‹ค์‹œ ์‹œ์ž‘ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ํƒ์ƒ‰ ๊ณต๊ฐ„์€ 16์˜ 64์Šน์ด ์•„๋‹ˆ๋ผ 2์˜ 16์Šน์งœ๋ฆฌ ๋ฌธ์ œ 16๊ฐœ๋‹ค. ์ด๊ฑธ ์•Œ์•„์ฑ„๋ฉด ๋ฌธ์ œ๊ฐ€ ์‚ฌ์‹ค์ƒ ๋๋‚œ๋‹ค.



๐ŸŒฒ ํ‘œ๋ฅผ ์—ด์–ด ๋ณธ๋‹ค

๊ธฐ๋Œ€๊ฐ’ 16๊ฐœ์™€ ํ‘œ ์•ž๋จธ๋ฆฌ๋ฅผ ์ง์ ‘ ๊บผ๋ƒˆ๋‹ค.

#!/usr/bin/env python3
"""main ์˜ .data ์—์„œ ๋‘ ํ‘œ๋ฅผ ๊บผ๋‚ด ๋ณธ๋‹ค: expected[16] ๊ณผ tree[524288] ์˜ ์•ž๋จธ๋ฆฌ."""
import struct
from pathlib import Path
 
blob = (Path(__file__).with_name("extracted") / "main").read_bytes()
off = lambda va: va - 0x4000 + 0x3000          # .data vaddr 0x4000 -> file 0x3000
 
expected = struct.unpack("<16i", blob[off(0x4020):off(0x4020) + 64])
print("expected[16] @ .data+0x20")
for i in range(0, 16, 4):
    print("  [%2d..%2d]  %s" % (i, i + 3, "  ".join("0x%08x" % (x & 0xFFFFFFFF) for x in expected[i:i + 4])))
 
head = struct.unpack("<32i", blob[off(0x4060):off(0x4060) + 128])
print("\ntree[0..31] @ .data+0x60   (2*node+bit ๋กœ ์ƒ‰์ธ, -1 ์€ ๋นˆ ์Šฌ๋กฏ)")
for i in range(0, 32, 8):
    print("  [%2d..%2d]  %s" % (i, i + 7, " ".join("%9d" % x for x in head[i:i + 8])))
 
print("\n๋ฃจํŠธ์˜ ๋‘ ์ž์‹ = tree[0]=%d, tree[1]=%d" % (head[0], head[1]))
print("ํŒŒ์ผ ์ „์ฒด์—์„œ 0xff ๋ฐ”์ดํŠธ ๋น„์œจ = %.1f%%  (๋นˆ ์Šฌ๋กฏ -1 ์ด ๊ทธ๋งŒํผ ๋งŽ๋‹ค)"
      % (100 * blob.count(0xFF) / len(blob)))
python3 dump_tables.py

dump_tables.py ์ถœ๋ ฅ โ€” expected 16๊ฐœ ๊ฐ’๊ณผ ํ‘œ ์•ž๋จธ๋ฆฌ 32์นธ. ๋ฃจํŠธ์˜ ๋‘ ์ž์‹์€ 72254 ์™€ 116475 ์ด๊ณ  ํŒŒ์ผ์˜ 74.5ํผ์„ผํŠธ๊ฐ€ 0xff ๋‹ค
dump_tables.py ์ถœ๋ ฅ โ€” expected 16๊ฐœ ๊ฐ’๊ณผ ํ‘œ ์•ž๋จธ๋ฆฌ 32์นธ. ๋ฃจํŠธ์˜ ๋‘ ์ž์‹์€ 72254 ์™€ 116475 ์ด๊ณ  ํŒŒ์ผ์˜ 74.5ํผ์„ผํŠธ๊ฐ€ 0xff ๋‹ค

-1 ์ด ๋ˆˆ์— ๋ฐŸํžŒ๋‹ค. ์•ž 32์นธ ์ค‘ ์ ˆ๋ฐ˜ ์ด์ƒ์ด -1 ์ด๊ณ , ํŒŒ์ผ ์ „์ฒด ๋ฐ”์ดํŠธ์˜ 74.5%๊ฐ€ 0xff ๋‹ค. ํ‘œ๊ฐ€ ์„ฑ๊ธฐ๊ฒŒ ์ฐจ ์žˆ๋‹ค๋Š” ๋œป์ธ๋ฐ, ์–ผ๋งˆ๋‚˜ ์„ฑ๊ธด์ง€๋Š” ์„ธ์–ด ๋ด์•ผ ํ•œ๋‹ค.

๋ฃจํŠธ์—์„œ ์‹ค์ œ๋กœ ๋‹ฟ๋Š” ๋…ธ๋“œ๋งŒ ๊นŠ์ด๋ณ„๋กœ ์„ธ๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

#!/usr/bin/env python3
"""Branches and Leaves โ€” .data ์— ๋ฐ•ํžŒ ํ‘œ๊ฐ€ ์ •๋ง 'ํŠธ๋ฆฌ'์ธ์ง€ ์„ธ์–ด ๋ณธ๋‹ค."""
import struct
from collections import Counter
from pathlib import Path
 
BIN = Path(__file__).with_name("extracted") / "main"
DATA_VADDR, DATA_OFF = 0x4000, 0x3000
TREE_VA, TREE_N = 0x4060, (0x204060 - 0x4060) // 4
NODE_MAX = 0x3FFFF
 
blob = BIN.read_bytes()
o = TREE_VA - DATA_VADDR + DATA_OFF
tree = struct.unpack("<%di" % TREE_N, blob[o:o + TREE_N * 4])
 
print("[table] ์—”ํŠธ๋ฆฌ %d๊ฐœ = %.1f MB" % (TREE_N, TREE_N * 4 / 1048576))
print("[table] ๊ฐ’ ๋ฒ”์œ„ min=%d max=%d" % (min(tree), max(tree)))
print("[table] ์Œ์ˆ˜ ์—”ํŠธ๋ฆฌ %d๊ฐœ, ๊ทธ์ค‘ -1 ์ด %d๊ฐœ"
      % (sum(1 for x in tree if x < 0), sum(1 for x in tree if x == -1)))
print("[table] ๋‚ด๋ถ€๋…ธ๋“œ๋กœ ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ฐ’(0..0x3ffff) %d๊ฐœ" % sum(1 for x in tree if 0 <= x <= NODE_MAX))
 
# ๋ฃจํŠธ 0 ์—์„œ ์‹ค์ œ๋กœ ๋‹ฟ๋Š” ๋…ธ๋“œ๋งŒ ๊นŠ์ด๋ณ„๋กœ ์„ผ๋‹ค
level, reach, leaves = {0}, set(), []
for depth in range(16):
    nxt = set()
    for n in level:
        reach.add(n)
        for bit in (0, 1):
            child = tree[2 * n + bit]
            if depth == 15:
                leaves.append(child)
            else:
                nxt.add(child)
    print("[walk ] depth %2d: ๋…ธ๋“œ %6d๊ฐœ -> ์ž์‹ %6d๊ฐœ" % (depth, len(level), len(nxt)))
    level = nxt
 
used = len(reach) * 2
print("[reach] ์‹ค์ œ๋กœ ์ฝํžˆ๋Š” ๋‚ด๋ถ€๋…ธ๋“œ %d๊ฐœ (์—”ํŠธ๋ฆฌ %d๊ฐœ)" % (len(reach), used))
print("[reach] ํ•œ ๋ฒˆ๋„ ์•ˆ ์ฝํžˆ๋Š” ์—”ํŠธ๋ฆฌ %d๊ฐœ = %.1f%% ๊ฐ€ ๋ฏธ๋ผ" % (TREE_N - used, 100 * (TREE_N - used) / TREE_N))
print("[reach] ์“ฐ์ธ ๋…ธ๋“œ ์ธ๋ฑ์Šค min=%d max=%d (0x3ffff ๊นŒ์ง€ ํฉ์–ด๋†“์•˜๋‹ค)" % (min(reach), max(reach)))
print("[leaf ] ๋ฆฌํ”„ %d๊ฐœ ์ค‘ ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ’ %d๊ฐœ" % (len(leaves), len(set(leaves))))
print("[leaf ] ๋ฆฌํ”„ ์ตœ์†Œ 0x%08x / ์ตœ๋Œ€ 0x%08x โ€” ์ „๋ถ€ 0x3ffff ์ดˆ๊ณผ๋ผ ๊ฒฝ๊ณ„๊ฒ€์‚ฌ์— ๊ฑธ๋ฆฐ๋‹ค"
      % (min(leaves) & 0xFFFFFFFF, max(leaves) & 0xFFFFFFFF))
dup = [v for v, c in Counter(leaves).items() if c > 1]
print("[leaf ] ์ค‘๋ณต ๋ฆฌํ”„ %d๊ฐœ -> ์ „๋‹จ์‚ฌ์ด๋ฏ€๋กœ ๊ฐ ๊ธฐ๋Œ€๊ฐ’์˜ ์—ญ์ƒ์€ ์œ ์ผํ•˜๋‹ค" % len(dup))
python3 analyze_tree.py

analyze_tree.py ์ถœ๋ ฅ โ€” ๊นŠ์ด๋งˆ๋‹ค ๋…ธ๋“œ๊ฐ€ ์ •ํ™•ํžˆ ๋‘ ๋ฐฐ๋กœ ๋Š˜์–ด 65535 ๊ฐœ๊ฐ€ ๋˜๊ณ  ๋ฆฌํ”„ 65536 ๊ฐœ๊ฐ€ ์ „๋ถ€ ์„œ๋กœ ๋‹ค๋ฅด๋ฉฐ ํ‘œ์˜ 75ํผ์„ผํŠธ๋Š” ํ•œ ๋ฒˆ๋„ ์•ˆ ์ฝํžŒ๋‹ค
analyze_tree.py ์ถœ๋ ฅ โ€” ๊นŠ์ด๋งˆ๋‹ค ๋…ธ๋“œ๊ฐ€ ์ •ํ™•ํžˆ ๋‘ ๋ฐฐ๋กœ ๋Š˜์–ด 65535 ๊ฐœ๊ฐ€ ๋˜๊ณ  ๋ฆฌํ”„ 65536 ๊ฐœ๊ฐ€ ์ „๋ถ€ ์„œ๋กœ ๋‹ค๋ฅด๋ฉฐ ํ‘œ์˜ 75ํผ์„ผํŠธ๋Š” ํ•œ ๋ฒˆ๋„ ์•ˆ ์ฝํžŒ๋‹ค

์ฝ์–ด ๋‚ธ ๊ฒƒ ๋„ค ๊ฐ€์ง€.

  • ๊นŠ์ด๋งˆ๋‹ค ๋…ธ๋“œ ์ˆ˜๊ฐ€ ์ •ํ™•ํžˆ ๋‘ ๋ฐฐ๋กœ ๋Š˜์–ด๋‚œ๋‹ค. 1, 2, 4, ... 32768. ํ•ฉ์น˜๋Š” ๊ฐ€์ง€๊ฐ€ ํ•˜๋‚˜๋„ ์—†๋Š” ์™„์ „ ์ด์ง„ํŠธ๋ฆฌ๋‹ค.

  • ์‹ค์ œ๋กœ ์ฝํžˆ๋Š” ๋‚ด๋ถ€ ๋…ธ๋“œ๋Š” 65,535๊ฐœ(์—”ํŠธ๋ฆฌ 131,070์นธ). ๋‚˜๋จธ์ง€ 393,218์นธ์€ ์ „๋ถ€ -1 ์ด๊ณ  ํ•œ ๋ฒˆ๋„ ์•ˆ ์ฝํžŒ๋‹ค. ํ‘œ์˜ 75%๊ฐ€ ๊ทธ๋ƒฅ ๋นˆ์นธ์ด๋ผ ํŒŒ์ผ์˜ 74.5%๊ฐ€ 0xff ์˜€๋˜ ๊ฒƒ์ด๋‹ค.

  • ๋…ธ๋“œ ๋ฒˆํ˜ธ๋Š” 0๋ถ€ํ„ฐ 262,139๊นŒ์ง€ ํฉ์–ด์ ธ ์žˆ๋‹ค. ์ˆœ์„œ๋Œ€๋กœ ์ฑ„์› ์œผ๋ฉด 65,535๋ฒˆ๊นŒ์ง€๋กœ ์ถฉ๋ถ„ํ–ˆ๋‹ค. ์ƒํ•œ ๊ทผ์ฒ˜๊นŒ์ง€ ์ผ๋ถ€๋Ÿฌ ๋ฟŒ๋ ค ๋†”์„œ ํ‘œ๊ฐ€ 4๋ฐฐ๋กœ ๋ถ€ํ‘ผ ๊ฒƒ์ด๊ณ , ๊ทธ๊ฒŒ 2MB์˜ ์ •์ฒด๋‹ค.

  • ๋ฆฌํ”„ 65,536๊ฐœ๊ฐ€ ์ „๋ถ€ ์„œ๋กœ ๋‹ค๋ฅด๋‹ค.

๋งˆ์ง€๋ง‰ ์ค„์ด ์ด ๋ฌธ์ œ์˜ ๋‹ต์ด๋‹ค. 16๋น„ํŠธ ๊ฐ’์—์„œ ๋ฆฌํ”„๋กœ ๊ฐ€๋Š” ์‚ฌ์ƒ์ด ์ „๋‹จ์‚ฌ๋ฉด, ๊ธฐ๋Œ€๊ฐ’ ํ•˜๋‚˜๋งˆ๋‹ค ๊ทธ๊ฑธ ๋งŒ๋“œ๋Š” ๋ฉ์–ด๋ฆฌ๊ฐ€ ์ •ํ™•ํžˆ ํ•˜๋‚˜ ์žˆ๋‹ค. ํ›„๋ณด๊ฐ€ ๊ฐˆ๋ฆด ์ผ์ด ์—†๋‹ค.

๋ฉ์–ด๋ฆฌ ํ•˜๋‚˜๊ฐ€ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ๋‚ด๋ ค๊ฐ€๋Š”์ง€ ๊ทธ๋ ค ๋ณด๋ฉด ์ด๋ ‡๋‹ค.

๋ฉ์–ด๋ฆฌ d8f7 ์ด 16๋น„ํŠธ๋กœ ํŽด์ง„ ๋’ค ๋ฃจํŠธ 0 ์—์„œ 116475 ์™€ 110380 ์„ ๊ฑฐ์ณ 16์Šคํ… ๋งŒ์— ๋ฆฌํ”„ 0x6e42db36 ์— ๋‹ฟ์•„ ๊ธฐ๋Œ€๊ฐ’ 0๋ฒˆ๊ณผ ๋งž๋Š” ๊ฒฝ๋กœ ๊ทธ๋ฆผ
๋ฉ์–ด๋ฆฌ d8f7 ์ด 16๋น„ํŠธ๋กœ ํŽด์ง„ ๋’ค ๋ฃจํŠธ 0 ์—์„œ 116475 ์™€ 110380 ์„ ๊ฑฐ์ณ 16์Šคํ… ๋งŒ์— ๋ฆฌํ”„ 0x6e42db36 ์— ๋‹ฟ์•„ ๊ธฐ๋Œ€๊ฐ’ 0๋ฒˆ๊ณผ ๋งž๋Š” ๊ฒฝ๋กœ ๊ทธ๋ฆผ



๐Ÿ› ํŠธ๋ฆฌ๊ฐ€ ์ „๋‹จ์‚ฌ๋ผ์„œ ์ƒ๊ธฐ๋Š” ํ•จ์ •

โ–ถ๐Ÿ› ์‚ฝ์งˆ โ€” ํ›„๋ณด๊ฐ€ ํ•˜๋‚˜ ๋‚˜์™”๋‹ค๋Š” ๊ฑด ์•„๋ฌด ๋ณด์ฆ๋„ ์•„๋‹ˆ๋‹ค

์ „๋‹จ์‚ฌ๋ผ๋Š” ์„ฑ์งˆ์ด ํŽธํ•˜๊ธฐ๋งŒ ํ•œ ๊ฑด ์•„๋‹ˆ๋‹ค. ์ฝ๊ธฐ๋ฅผ ํ‹€๋ ค๋„ ํ›„๋ณด๊ฐ€ ๋˜‘๊ฐ™์ด ํ•˜๋‚˜์”ฉ ๋‚˜์˜จ๋‹ค.

๋น„ํŠธ ์ˆœ์„œ๋‚˜ ๋‹ˆ๋ธ” ์ˆœ์„œ๋ฅผ ์ž˜๋ชป ์žก์•„๋„ ๊ทธ๊ฑด ๊ทธ๋ƒฅ "๋‹ค๋ฅธ ๋ผ๋ฒจ๋ง"์ผ ๋ฟ์ด๋ผ ์—ฌ์ „ํžˆ ์ „๋‹จ์‚ฌ๋‹ค. ๊ทธ๋ž˜์„œ ์‹คํŒจ ์‹ ํ˜ธ๊ฐ€ ์•ˆ ๋œฌ๋‹ค. ํ›„๋ณด 0๊ฐœ๋กœ ์ฃฝ์–ด ์ฃผ๋ฉด ์˜คํžˆ๋ ค ๊ณ ๋ง™๋‹ค.

์‹ค์ œ๋กœ ๋‘ ๊ฐ€์ง€๋ฅผ ํ‹€๋ฆฌ๊ฒŒ ๋งŒ๋“ค์–ด ๋Œ๋ ค ๋ดค๋‹ค.

#!/usr/bin/env python3
"""์ฝ๊ธฐ๋ฅผ ํ•œ ๊ตฐ๋ฐ์”ฉ ํ‹€๋ฆฌ๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€ โ€” ์‹ค์ œ๋กœ ๋Œ๋ ค ๋ณธ๋‹ค.
 
์„ธ ๋ชจ๋ธ ๋ชจ๋‘ 'ํ›„๋ณด ์—†์Œ'์œผ๋กœ ์ฃฝ์ง€ ์•Š๋Š”๋‹ค. ํŠธ๋ฆฌ๊ฐ€ ์ „๋‹จ์‚ฌ๋ผ์„œ **ํ‹€๋ฆฐ ๋ชจ๋ธ๋„
64๊ธ€์ž์งœ๋ฆฌ ๊ทธ๋Ÿด๋“ฏํ•œ ๋‹ต์„ ํ•˜๋‚˜์”ฉ ๋งŒ๋“ค์–ด ๋‚ธ๋‹ค.** ๊ทธ๋ž˜์„œ ๋‹ต์ด ๋‚˜์™”๋‹ค๋Š” ์‚ฌ์‹ค์€
๊ฒ€์ฆ์ด ๋ชป ๋œ๋‹ค โ€” ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋„ฃ์–ด ๋ด์•ผ ์•ˆ๋‹ค.
"""
import struct
import subprocess
from pathlib import Path
 
HERE = Path(__file__).parent
BIN = HERE / "extracted" / "main"
blob = BIN.read_bytes()
off = lambda va: va - 0x4000 + 0x3000
expected = struct.unpack("<16i", blob[off(0x4020):off(0x4020) + 64])
tree = struct.unpack("<524288i", blob[off(0x4060):off(0x4060) + 524288 * 4])
 
 
def build(bit_msb_first):
    """v -> 16์Šคํ… ๋’ค leaf. bit_msb_first ๋ฉด ์ƒ์œ„ ๋น„ํŠธ๋ถ€ํ„ฐ ์†Œ๋น„ํ•œ๋‹ค."""
    table = {}
    for v in range(1 << 16):
        node, x = 0, v
        for _ in range(16):
            bit = (x >> 15) & 1 if bit_msb_first else x & 1
            node = tree[2 * node + bit]
            x = (x << 1) & 0xFFFF if bit_msb_first else x >> 1
        table[node] = v
    return table
 
 
def answer(table, nibble_reversed):
    out = []
    for want in expected:
        v = table.get(want)
        if v is None:
            return None
        s = "%04x" % v
        out.append(s[::-1] if nibble_reversed else s)
    return "".join(out)
 
 
def run(s):
    r = subprocess.run([str(BIN), s], capture_output=True, text=True)
    return r.returncode, r.stdout.strip()
 
 
lsb, msb = build(False), build(True)
for label, cand in (
    ("A. ๋น„ํŠธ๋ฅผ MSB ๋ถ€ํ„ฐ ์†Œ๋น„", answer(msb, False)),
    ("B. ํ•œ ๋ฉ์–ด๋ฆฌ ์•ˆ ๋‹ˆ๋ธ” ์ˆœ์„œ๋ฅผ ๋’ค์ง‘์Œ", answer(lsb, True)),
    ("C. ์ œ๋Œ€๋กœ ์ฝ์€ ๊ฒƒ", answer(lsb, False)),
):
    rc, out = run(cand)
    print("%-28s %s" % (label, cand))
    print("%-28s -> exit=%d  stdout=%s" % ("", rc, out if out else "(์—†์Œ)"))
python3 traps.py

traps.py ์ถœ๋ ฅ โ€” ๋น„ํŠธ ์ˆœ์„œ์™€ ๋‹ˆ๋ธ” ์ˆœ์„œ๋ฅผ ํ‹€๋ฆฌ๊ฒŒ ์ฝ์–ด๋„ 64๊ธ€์ž ๋‹ต์ด ๋‚˜์˜ค์ง€๋งŒ ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” ๋‘˜ ๋‹ค exit 1 ๋กœ ๊ฑฐ์ ˆํ•˜๊ณ  ์ œ๋Œ€๋กœ ์ฝ์€ ๊ฒƒ๋งŒ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋‚ธ๋‹ค
traps.py ์ถœ๋ ฅ โ€” ๋น„ํŠธ ์ˆœ์„œ์™€ ๋‹ˆ๋ธ” ์ˆœ์„œ๋ฅผ ํ‹€๋ฆฌ๊ฒŒ ์ฝ์–ด๋„ 64๊ธ€์ž ๋‹ต์ด ๋‚˜์˜ค์ง€๋งŒ ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” ๋‘˜ ๋‹ค exit 1 ๋กœ ๊ฑฐ์ ˆํ•˜๊ณ  ์ œ๋Œ€๋กœ ์ฝ์€ ๊ฒƒ๋งŒ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋‚ธ๋‹ค

A๋Š” sar edx,1 ์˜ ๋ฐฉํ–ฅ์„ ๋ฐ˜๋Œ€๋กœ ์ฝ์€ ๊ฒƒ์ด๊ณ , B๋Š” shl edx,0x4 ๊ฐ€ ๋‹ˆ๋ธ”์„ ์–ด๋А ์ชฝ๋ถ€ํ„ฐ ์Œ“๋Š”์ง€ ๋’ค์ง‘์€ ๊ฒƒ์ด๋‹ค. ๋‘˜ ๋‹ค ์†Œ๋ฌธ์ž 16์ง„์ˆ˜ 64๊ธ€์ž๊ฐ€ ๋ฉ€์ฉกํ•˜๊ฒŒ ๋‚˜์˜จ๋‹ค. ๋ˆˆ์œผ๋กœ๋Š” ์ •๋‹ต๊ณผ ๊ตฌ๋ถ„์ด ์•ˆ ๋œ๋‹ค.

๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋„ฃ์–ด์•ผ ๊ฐˆ๋ฆฐ๋‹ค. A์™€ B๋Š” exit=1, ์ œ๋Œ€๋กœ ์ฝ์€ C๋งŒ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋‚ธ๋‹ค.

๊ทธ๋ž˜์„œ solve ์Šคํฌ๋ฆฝํŠธ๊ฐ€ "candidates=1" ์„ 16๋ฒˆ ์ฐ๋Š” ๊ฒƒ์€ ์ฝ๊ธฐ๊ฐ€ ๋งž๋‹ค๋Š” ๊ทผ๊ฑฐ๊ฐ€ ์•„๋‹ˆ๋‹ค. ๊ฒ€์ฆ์€ ๋ฌธ์ œ ๋ฐ”์ด๋„ˆ๋ฆฌํ•œํ…Œ ์‹œ์ผœ์•ผ ํ•œ๋‹ค.


๐ŸŽฏ ํ’€์ด โ€” 65536 ๊ฒฝ๋กœ๋ฅผ ํ•œ ๋ฒˆ ํ›‘๋Š”๋‹ค

์—ญ๋ฐฉํ–ฅ์œผ๋กœ ํŠธ๋ฆฌ๋ฅผ ๋˜์งš์„ ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋ฉ์–ด๋ฆฌ ํ•˜๋‚˜๊ฐ€ 16๋น„ํŠธ๋ฟ์ด๋‹ˆ 65,536๊ฐœ๋ฅผ ์ „๋ถ€ ์•ž์œผ๋กœ ๊ตด๋ ค ๋ฆฌํ”„ โ†’ ๊ฐ’ ์—ญ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“ค๊ณ , ๊ธฐ๋Œ€๊ฐ’ 16๊ฐœ๋ฅผ ์กฐํšŒํ•˜๋ฉด ๊ทธ๋งŒ์ด๋‹ค. ํ‘œ ์ ‘๊ทผ์ด 100๋งŒ ๋ฒˆ์ฏค์ธ๋ฐ ํŒŒ์ด์ฌ์œผ๋กœ๋„ ์ˆœ์‹๊ฐ„์ด๋‹ค.

#!/usr/bin/env python3
"""Branches and Leaves (DreamHack, Silver 3, reversing) โ€” solver.
 
check() at 0x11e0 splits the 64-char argv[1] into 16 chunks of 4 hex digits,
turns each chunk into a 16-bit integer v, then walks an array-encoded binary
tree stored in .data: node = tree[2*node + bit], consuming the bits of v from
LSB to MSB, exactly 16 times.  The node reached after the 16th step must equal
expected[chunk] (16 int32 at .data+0x20).
 
Since every chunk is independent and only 16 bits wide, we replay all 65536
paths forward and index them by the leaf they reach.
"""
import struct
import sys
from pathlib import Path
 
BIN = Path(__file__).with_name("extracted") / "main"
 
DATA_VADDR = 0x4000     # .data virtual address
DATA_OFF = 0x3000       # .data file offset
EXPECT_VA = 0x4020      # int32 expected[16]
TREE_VA = 0x4060        # int32 tree[524288]
TREE_N = (0x204060 - 0x4060) // 4
DEPTH = 16              # bits consumed per chunk == loop counter in ecx
NODE_MAX = 0x3FFFF      # `cmp eax,0x3ffff; jg fail` bound on internal nodes
 
 
def load():
    blob = BIN.read_bytes()
    off = lambda va: va - DATA_VADDR + DATA_OFF
    expected = struct.unpack("<16i", blob[off(EXPECT_VA):off(EXPECT_VA) + 64])
    tree = struct.unpack("<%di" % TREE_N, blob[off(TREE_VA):off(TREE_VA) + TREE_N * 4])
    return expected, tree
 
 
def walk(tree, v):
    """Replay the 0x124c loop: 16 x  node = tree[2*node + (v & 1)], v >>= 1."""
    node = 0
    for step in range(DEPTH):
        if step and not (-1 < node <= NODE_MAX):   # bounds check at 0x1240
            return None
        node = tree[2 * node + (v & 1)]
        v >>= 1
    return node
 
 
def main():
    expected, tree = load()
 
    leaves = {}
    for v in range(1 << DEPTH):
        leaf = walk(tree, v)
        if leaf is None:
            continue
        leaves.setdefault(leaf, []).append(v)
 
    print("[*] tree entries      : %d" % TREE_N)
    print("[*] distinct leaves   : %d reached out of 65536 paths" % len(leaves))
 
    chunks = []
    for i, want in enumerate(expected):
        cands = leaves.get(want, [])
        print("[%2d] expected=0x%08x  candidates=%d  ->  %s"
              % (i, want & 0xFFFFFFFF, len(cands),
                 " ".join("%04x" % c for c in cands) or "NONE"))
        if len(cands) != 1:
            sys.exit("chunk %d is not uniquely determined" % i)
        chunks.append("%04x" % cands[0])
 
    key = "".join(chunks)
    print("[+] input : %s" % key)
    print("[+] flag  : DH{%s}" % key)
 
 
if __name__ == "__main__":
    main()
python3 solve.py

solve.py ์ถœ๋ ฅ โ€” 65536 ๊ฒฝ๋กœ๋ฅผ ํ•œ ๋ฒˆ ํ›‘์–ด ๋งŒ๋“  ์—ญ์ธ๋ฑ์Šค๋กœ ๊ธฐ๋Œ€๊ฐ’ 16๊ฐœ๋ฅผ ์กฐํšŒํ•˜๋ฉด ๋ฉ์–ด๋ฆฌ๋งˆ๋‹ค ํ›„๋ณด๊ฐ€ ์ •ํ™•ํžˆ ํ•˜๋‚˜์”ฉ ๋‚˜์˜จ๋‹ค
solve.py ์ถœ๋ ฅ โ€” 65536 ๊ฒฝ๋กœ๋ฅผ ํ•œ ๋ฒˆ ํ›‘์–ด ๋งŒ๋“  ์—ญ์ธ๋ฑ์Šค๋กœ ๊ธฐ๋Œ€๊ฐ’ 16๊ฐœ๋ฅผ ์กฐํšŒํ•˜๋ฉด ๋ฉ์–ด๋ฆฌ๋งˆ๋‹ค ํ›„๋ณด๊ฐ€ ์ •ํ™•ํžˆ ํ•˜๋‚˜์”ฉ ๋‚˜์˜จ๋‹ค

distinct leaves : 65536 reached out of 65536 paths โ€” ๊ฒฝ๋กœ๊ฐ€ ํ•˜๋‚˜๋„ ์•ˆ ์ฃฝ๊ณ  ๋ฆฌํ”„๋„ ์•ˆ ๊ฒน์นœ๋‹ค. ์•ž์˜ ํ†ต๊ณ„์™€ ๊ฐ™์€ ๋ง์ด๋‹ค.

๊ธฐ๋Œ€๊ฐ’ 16๊ฐœ๊ฐ€ ์ „๋ถ€ candidates=1 ๋กœ ๋–จ์–ด์ง€๊ณ , ์ด์–ด ๋ถ™์ด๋ฉด 64๊ธ€์ž๋‹ค. ์‹ค์ธก 0.21์ดˆ.



๐Ÿงช ๋Ÿฐํƒ€์ž„์œผ๋กœ ํ•œ ๋ฒˆ ๋” ํ™•์ธ

์ •์ ์œผ๋กœ ์ฝ์€ ๊ฒŒ ์‹คํ–‰ ์ค‘์—๋„ ๋งž๋Š”์ง€ ๋ณธ๋‹ค. ๋น„๊ต ์ง€์  0x1269 ์— ๋ธŒ๋ ˆ์ดํฌ๋ฅผ ๊ฑธ๊ณ  eax(16์Šคํ… ๋’ค ๊ฐ’)์™€ expected[i] ๋ฅผ ๊ฐ™์ด ์ฐ์—ˆ๋‹ค.

PIE๋ผ ๋ฒ ์ด์Šค ์ฃผ์†Œ๋Š” ๊ด€๋ก€๋กœ ๊ฐ€์ •ํ•˜์ง€ ์•Š๊ณ  info proc mappings ์—์„œ ์‹ค์ธกํ•ด ์“ด๋‹ค.

set pagination off
set confirm off
starti
python
import gdb
base = None
for line in gdb.execute("info proc mappings", to_string=True).splitlines():
    if line.rstrip().endswith("/main"):
        base = int(line.split()[0], 16)
        break
gdb.execute("set $base = %d" % base)
print("[gdb] PIE base = 0x%x  (info proc mappings ๋กœ ์‹ค์ธก)" % base)
end
break *($base + 0x1269)
commands
silent
printf "chunk %2d: 16์Šคํ… ๋’ค node=0x%08x   expected[%d]=0x%08x   %s\n", $r9d/4, $eax, $r9d/4, *(int*)($r10 + ($r9d>>2)*4), ($eax == *(int*)($r10 + ($r9d>>2)*4)) ? "์ผ์น˜" : "๋ถˆ์ผ์น˜"
continue
end
continue
gdb -q -batch -x cmds.gdb --args ./extracted/main d8f794325872bab95cfaa117545c6c0b77059a74f4dd31d8c2bf130a59745d77

gdb ๋ฐฐ์น˜ ์‹คํ–‰ โ€” PIE ๋ฒ ์ด์Šค๋ฅผ info proc mappings ๋กœ ์‹ค์ธกํ•ด ๋น„๊ต ์ง€์ ์— ๋ธŒ๋ ˆ์ดํฌ๋ฅผ ๊ฑธ์—ˆ๊ณ  16๋ฉ์–ด๋ฆฌ ์ „๋ถ€ ๊ธฐ๋Œ€๊ฐ’๊ณผ ์ผ์น˜ํ•œ ๋’ค ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค
gdb ๋ฐฐ์น˜ ์‹คํ–‰ โ€” PIE ๋ฒ ์ด์Šค๋ฅผ info proc mappings ๋กœ ์‹ค์ธกํ•ด ๋น„๊ต ์ง€์ ์— ๋ธŒ๋ ˆ์ดํฌ๋ฅผ ๊ฑธ์—ˆ๊ณ  16๋ฉ์–ด๋ฆฌ ์ „๋ถ€ ๊ธฐ๋Œ€๊ฐ’๊ณผ ์ผ์น˜ํ•œ ๋’ค ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค

๋ฒ ์ด์Šค๋Š” 0x555555554000, ๋ธŒ๋ ˆ์ดํฌํฌ์ธํŠธ๋Š” 0x555555555269. 16๋ฉ์–ด๋ฆฌ๊ฐ€ ์ „๋ถ€ ์ผ์น˜ํ•˜๊ณ  ๊ทธ๋Œ€๋กœ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋‚˜์˜จ๋‹ค. ์ •์  ๋ถ„์„๊ณผ ์‹คํ–‰์ด ์–ด๊ธ‹๋‚˜๋Š” ๊ณณ์€ ์—†๋‹ค.



๐Ÿš€ ์žฌํ˜„

๊ฒฝ๊ณ„ ์กฐ๊ฑด๋„ ๊ฐ™์ด ํ™•์ธํ•œ๋‹ค. ํ•œ ๊ธ€์ž๋งŒ ๋ฐ”๊พธ๊ฑฐ๋‚˜, ๋Œ€๋ฌธ์ž๋กœ ์“ฐ๊ฑฐ๋‚˜, 63๊ธ€์ž๋กœ ์ค„์ด๋ฉด ์ „๋ถ€ exit=1 ์ด์–ด์•ผ ํ•œ๋‹ค.

#!/usr/bin/env python3
"""solve.py ๊ฐ€ ๋ฝ‘์€ ์ž…๋ ฅ์„ ๋ฌธ์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๊ทธ๋Œ€๋กœ ๋„ฃ์–ด ๋ณธ๋‹ค."""
import subprocess
from pathlib import Path
 
BIN = Path(__file__).with_name("extracted") / "main"
KEY = "d8f794325872bab95cfaa117545c6c0b77059a74f4dd31d8c2bf130a59745d77"
 
for label, arg in (("์ •๋‹ต", KEY),
                   ("ํ•œ ๊ธ€์ž ๋ฐ”๊ฟˆ", KEY[:-1] + ("0" if KEY[-1] != "0" else "1")),
                   ("๋Œ€๋ฌธ์ž๋กœ", KEY.upper()),
                   ("63๊ธ€์ž", KEY[:-1])):
    r = subprocess.run([str(BIN), arg], capture_output=True, text=True)
    print("%-12s exit=%d  stdout=%s" % (label, r.returncode, r.stdout.strip() or "(์—†์Œ)"))
python3 verify.py

verify.py ์ถœ๋ ฅ โ€” ์ •๋‹ต๋งŒ exit 0 ์œผ๋กœ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋‚ด๊ณ  ํ•œ ๊ธ€์ž๋ฅผ ๋ฐ”๊พธ๊ฑฐ๋‚˜ ๋Œ€๋ฌธ์ž๋กœ ์“ฐ๊ฑฐ๋‚˜ 63๊ธ€์ž๋กœ ์ค„์ด๋ฉด ์ „๋ถ€ exit 1 ์ด๋‹ค
verify.py ์ถœ๋ ฅ โ€” ์ •๋‹ต๋งŒ exit 0 ์œผ๋กœ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋‚ด๊ณ  ํ•œ ๊ธ€์ž๋ฅผ ๋ฐ”๊พธ๊ฑฐ๋‚˜ ๋Œ€๋ฌธ์ž๋กœ ์“ฐ๊ฑฐ๋‚˜ 63๊ธ€์ž๋กœ ์ค„์ด๋ฉด ์ „๋ถ€ exit 1 ์ด๋‹ค

๋Œ€๋ฌธ์ž๊ฐ€ ํŠ•๊ธฐ๋Š” ๊ฒŒ ๋ˆˆ์— ๋ณด์ธ๋‹ค. ์•ž์—์„œ ์ฝ์€ 'a'-'f' ์ „์šฉ ๋ถ„๊ธฐ ๊ทธ๋Œ€๋กœ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฐฐํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ๋งŒ ๊ฐ€์ง€๊ณ  ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ๋„๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‚จ๊ธด๋‹ค. ํ‘œ๋ฅผ ๋‹ค์‹œ ์ฝ์–ด ์ž…๋ ฅ์„ ๋ณต์›ํ•˜๊ณ , ๊ทธ ์ž…๋ ฅ์„ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋˜๋จน์—ฌ ๊ธฐ๋Œ€ ํ”Œ๋ž˜๊ทธ์™€ ๋Œ€์กฐ๊นŒ์ง€ ํ•œ๋‹ค.

#!/usr/bin/env bash
# Branches and Leaves (DreamHack Silver 3, reversing) โ€” ํ•œ ๋ฐฉ ์žฌํ˜„.
# ๋ฐฐํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ .data ์—์„œ ํŠธ๋ฆฌ๋ฅผ ์ฝ์–ด ์ž…๋ ฅ์„ ๋ณต์›ํ•˜๊ณ , ๊ทธ ์ž…๋ ฅ์„ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋‹ค์‹œ ๋จน์—ฌ flag ๋ฅผ ๋ฐ›๋Š”๋‹ค.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
 
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ œ.json'))['flag'])" 2>/dev/null || echo '')
 
chmod +x extracted/main
KEY=$(timeout 300 python3 solve.py | sed -n 's/^\[+\] input : //p')
if [ -z "$KEY" ]; then
  echo "โŒ FAIL (solve.py ๊ฐ€ ์ž…๋ ฅ์„ ๋ชป ๋ฝ‘์•˜๋‹ค)"; exit 1
fi
echo "๋ณต์›ํ•œ ์ž…๋ ฅ: $KEY"
 
OUT=$(./extracted/main "$KEY" || true)
echo "$OUT"
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
 
if [ -n "$FLAG" ] && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
  echo; echo "โœ… PASS  $FLAG"; exit 0
fi
echo; echo "โŒ FAIL (์–ป์€ ๊ฐ’: '${FLAG:-์—†์Œ}' / ๊ธฐ๋Œ€: '$EXPECT')"; exit 1
./reproduce.sh

reproduce.sh ์ถœ๋ ฅ โ€” ๋ฐฐํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ํ‘œ๋ฅผ ๋‹ค์‹œ ์ฝ์–ด ์ž…๋ ฅ์„ ๋ณต์›ํ•˜๊ณ  ๊ทธ ์ž…๋ ฅ์„ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋˜๋จน์—ฌ ๊ธฐ๋Œ€ ํ”Œ๋ž˜๊ทธ์™€ ์ผ์น˜ํ•˜๋Š”์ง€๊นŒ์ง€ ํ™•์ธํ•œ๋‹ค
reproduce.sh ์ถœ๋ ฅ โ€” ๋ฐฐํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ํ‘œ๋ฅผ ๋‹ค์‹œ ์ฝ์–ด ์ž…๋ ฅ์„ ๋ณต์›ํ•˜๊ณ  ๊ทธ ์ž…๋ ฅ์„ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋˜๋จน์—ฌ ๊ธฐ๋Œ€ ํ”Œ๋ž˜๊ทธ์™€ ์ผ์น˜ํ•˜๋Š”์ง€๊นŒ์ง€ ํ™•์ธํ•œ๋‹ค

๋กœ์ปฌ ํ™˜๊ฒฝ์€ Ubuntu 25.10 x86_64, Python 3.13.7, GNU gdb 16.3, binutils objdump 2.45๋‹ค. ๋นŒ๋“œ ๊ณผ์ •์€ ์—†๋‹ค โ€” ๋ฐฐํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๊ทธ๋Œ€๋กœ ์ฝ๊ณ  ๊ทธ๋Œ€๋กœ ์‹คํ–‰ํ•œ๋‹ค.

์ตœ์ข… ํ”Œ๋ž˜๊ทธ.

DH{d8f794325872bab95cfaa117545c6c0b77059a74f4dd31d8c2bf130a59745d77}

๐Ÿ“ ๊ฒฐ๋ก 

ํŒŒ์ผ์ด ํฌ๋‹ค๊ณ  ์–ด๋ ค์šด ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋‹ค

2.1MB ์ค‘ 1.5MB๊ฐ€ 0xff ํŒจ๋”ฉ์ด์—ˆ๋‹ค. ํฌ๊ธฐ์— ๋ˆŒ๋ ค์„œ "์ •์  ๋งํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ์ธ๊ฐ€" ํ•˜๊ณ  ๋””์ปดํŒŒ์ผ๋Ÿฌ๋ถ€ํ„ฐ ์ผฐ๋‹ค๋ฉด ํ•œ์ฐธ ๋Œ์•„๊ฐ”์„ ๊ฒƒ์ด๋‹ค. ์„น์…˜ ํฌ๊ธฐ ํ•œ ์ค„์ด ๊ทธ๊ฑธ ๋จผ์ € ์ •๋ฆฌํ•ด ์ค€๋‹ค. .text ๊ฐ€ 509๋ฐ”์ดํŠธ๋ฉด ์†์œผ๋กœ ์ฝ๋Š” ๊ฒŒ ์ œ์ผ ๋น ๋ฅด๋‹ค.

๋ฉ์–ด๋ฆฌ๊ฐ€ ๋…๋ฆฝ์ด๋ฉด ํƒ์ƒ‰ ๊ณต๊ฐ„์ด ๋ฌด๋„ˆ์ง„๋‹ค

64๊ธ€์ž ์ „์ฒด๋ฅผ ํ•œ ๋ฉ์–ด๋ฆฌ๋กœ ๋ณด๋ฉด ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ์ฒœ๋ฌธํ•™์ ์ด์ง€๋งŒ, ๋งค ๋ฉ์–ด๋ฆฌ๊ฐ€ node = 0 ์—์„œ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฌธ์ œ๋Š” 2์˜ 16์Šน์งœ๋ฆฌ 16๊ฐœ๋กœ ์ชผ๊ฐœ์ง„๋‹ค. ๊ฒ€์ฆ ๋ฃจํ”„๋ฅผ ์ฝ์„ ๋•Œ "์•ž์˜ ์ƒํƒœ๊ฐ€ ๋’ค๋กœ ์ด์–ด์ง€๋Š”๊ฐ€"๋ฅผ ๋จผ์ € ๋ณด๋Š” ์Šต๊ด€์ด ๊ฐ’์„ ํ•œ๋‹ค.

๋‹ต์ด ํ•˜๋‚˜ ๋‚˜์˜จ ๊ฒƒ์€ ๊ฒ€์ฆ์ด ์•„๋‹ˆ๋‹ค

์ด ๋ฌธ์ œ๋Š” ํ‹€๋ฆฌ๊ฒŒ ์ฝ์–ด๋„ ํ›„๋ณด๊ฐ€ ํ•˜๋‚˜์”ฉ ๋‚˜์˜จ๋‹ค. ๋น„ํŠธ ์ˆœ์„œ๋ฅผ ๋’ค์ง‘์€ ๋‹ต๋„ 64๊ธ€์ž 16์ง„์ˆ˜๋กœ ๊ทธ๋Ÿด๋“ฏํ•˜๊ฒŒ ์ƒ๊ฒผ๋‹ค. ์ตœ์ข… ํŒ์ •์€ ๋ฌธ์ œ ๋ฐ”์ด๋„ˆ๋ฆฌํ•œํ…Œ ์‹œํ‚ค๋Š” ๊ฒŒ ๋งž๋‹ค. ๊ทธ๋ž˜์„œ reproduce.sh ๊ฐ€ ๋ณต์›ํ•œ ์ž…๋ ฅ์„ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋˜๋จน์ด๋Š” ๊ฒƒ์œผ๋กœ ๋๋‚˜๊ฒŒ ์งฐ๋‹ค.

๋งŒ๋“œ๋Š” ์ชฝ์—์„œ ๋ณด๋ฉด

๊ฒ€์‚ฌ๋ฅผ ์ฝ”๋“œ์—์„œ ๋ฐ์ดํ„ฐ๋กœ ์˜ฎ๊ธฐ๋ฉด ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ๋กœ ๋ณด์ด๋Š” ๋กœ์ง์ด ํ™• ์ค„์–ด๋“ ๋‹ค. ์—ฌ๊ธฐ์„œ๋„ ํŒ์ • ์ „์ฒด๊ฐ€ 60์ค„์ด ์•ˆ ๋œ๋‹ค. ๋‹ค๋งŒ ์˜ฎ๊ฒจ ๊ฐ„ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ตฌ์กฐ๋ฅผ ๊ทธ๋Œ€๋กœ ๋“œ๋Ÿฌ๋‚ธ๋‹ค๋Š” ๊ฒŒ ๋Œ€๊ฐ€๋‹ค. ๊นŠ์ด๋งˆ๋‹ค ๋…ธ๋“œ๊ฐ€ ๋‘ ๋ฐฐ์”ฉ ๋Š˜๊ณ  ๋ฆฌํ”„๊ฐ€ ์•ˆ ๊ฒน์นœ๋‹ค๋Š” ์‚ฌ์‹ค์€ ํ‘œ๋ฅผ ํ•œ ๋ฒˆ ์„ธ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ๋‚˜์˜ค๊ณ , ๊ทธ ์ˆœ๊ฐ„ ์—ญ์‚ฐ์ด 65,536ํšŒ ์ˆœํšŒ๋กœ ๋๋‚œ๋‹ค.

๋…ธ๋“œ ๋ฒˆํ˜ธ๋ฅผ ํฉ๋ฟŒ๋ ค ํ‘œ๋ฅผ 4๋ฐฐ๋กœ ๋ถ€ํ’€๋ฆฐ ๊ฒƒ๋„ ํŒŒ์ผ๋งŒ ํ‚ค์› ์ง€ ๋ถ„์„์„ ๋ง‰์ง€๋Š” ๋ชปํ–ˆ๋‹ค. ํ‘œ ๊ธฐ๋ฐ˜ ๊ฒ€์ฆ์„ ์ •๋ง ์–ด๋ ต๊ฒŒ ๋งŒ๋“ค๋ ค๋ฉด ๋ฉ์–ด๋ฆฌ๋ผ๋ฆฌ ์ƒํƒœ๋ฅผ ์ด์–ด ๋ถ™์ด๊ฑฐ๋‚˜ ๋ฆฌํ”„๋ฅผ ์—ฌ๋Ÿฌ ๊ฐ’์— ๊ฒน์น˜๊ฒŒ ํ•ด์„œ ์—ญ์ƒ์ด ๊ฐˆ๋ฆฌ๊ฒŒ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

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

Comments

๋Œ“๊ธ€

0๊ฐœ

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

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

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅˆ Silver 4] ํ™”๋ฉด์— ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๋œจ๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ flag ๋ฝ‘์•„๋‚ด๊ธฐ โ€” DreamHack fake ํ’€์ด
blog

[๐Ÿฅˆ Silver 4] ํ™”๋ฉด์— ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๋œจ๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ flag ๋ฝ‘์•„๋‚ด๊ธฐ โ€” DreamHack fake ํ’€์ด

์‹คํ–‰ํ•ด๋„ ์•„๋ฌด๊ฒƒ๋„ ์ถœ๋ ฅํ•˜์ง€ ์•Š๋Š” ELF ๋ฐ”์ด๋„ˆ๋ฆฌ ํ•˜๋‚˜. strace๋กœ ํ™•์ธํ•ด๋ณด๋‹ˆ read/write ๊ณ„์—ด syscall์ด ๋‹จ ํ•œ ๋ฒˆ๋„ ์—†๋‹ค โ€” ์ž„ํฌํŠธ๋œ ํ•จ์ˆ˜์กฐ์ฐจ malloc/free๋ฟ์ด๋‹ค. objdump๋กœ ๋œฏ์–ด๋ณด๋‹ˆ main()์€ ์Šคํƒ์— movabs ์ฆ‰์‹œ๊ฐ’์œผ๋กœ 23๋ฐ”์ดํŠธ ์‹œ๋“œ ๋ฒ„ํผ๋ฅผ ์กฐ๋ฆฝํ•ด mallocํ•œ ๋’ค, reverse โ†’ ์ธ์ ‘ ๋ฐ”์ดํŠธ ๊ตํ™˜ โ†’ XOR๋ผ๋Š” ์„ธ ๋‹จ๊ณ„ ๋ณ€ํ™˜๋งŒ ๊ฑฐ์ณ ๊ณง๋ฐ”๋กœ freeํ•ด๋ฒ„๋ฆฐ๋‹ค. ํ™”๋ฉด์— ์ฐํžˆ๋Š” ๊ฑด ์—†์ง€๋งŒ, ๊ทธ ๋ณ€ํ™˜ ๊ฒฐ๊ณผ๋ฅผ ํŒŒ์ด์ฌ์œผ๋กœ ๊ทธ๋Œ€๋กœ ์žฌํ˜„ํ•˜๊ณ  gdb๋กœ free() ์ง์ „ ํž™ ๋ฒ„ํผ๋ฅผ ์ง์ ‘ ๋“ค์—ฌ๋‹ค๋ณด๋Š” ๊ฒƒ์œผ๋กœ flag๋ฅผ ํ™•์ธํ–ˆ๋‹ค.
#dreamhack#ctf#reversing+4
2026-07-21#dreamhack +5
[๐Ÿฅˆ Silver 3] srand(0xBEEF) ํ•˜๋‚˜๋กœ 6MB ์‚ฌ์ง„์„ ๋˜์ฐพ๋‹ค โ€” DreamHack photographer ํ’€์ด
blog

[๐Ÿฅˆ Silver 3] srand(0xBEEF) ํ•˜๋‚˜๋กœ 6MB ์‚ฌ์ง„์„ ๋˜์ฐพ๋‹ค โ€” DreamHack photographer ํ’€์ด

BMP ํ•œ ์žฅ์„ ํ†ต์งธ๋กœ ๋ฐ”๊ฟ” ๋†“์€ 26KB์งœ๋ฆฌ C++ ๋ฐ”์ด๋„ˆ๋ฆฌ. ์‹œ๋“œ๊ฐ€ time(NULL)์ด ์•„๋‹ˆ๋ผ ์ƒ์ˆ˜ 0xBEEF๊ณ , ๋ฐ”์ดํŠธ๋งˆ๋‹ค rand()๋ฅผ ์ •ํ™•ํžˆ ํ•œ ๋ฒˆ์”ฉ๋งŒ ์“ฐ๋ฉฐ, ์„ธ ๊ฐˆ๋ž˜ ๋ณ€ํ™˜์ด ๋ชจ๋‘ ๊ฐ€์—ญ์ด๋‹ค. ๊ทธ ์„ธ ์กฐ๊ฑด์ด ๋งž์•„๋–จ์–ด์ ธ์„œ ์—ญํ•จ์ˆ˜๋ฅผ ๊ฐ™์€ ์ˆœ์„œ๋กœ ํ•œ ๋ฒˆ์”ฉ ๊ฑธ๋ฉด 6,220,854๋ฐ”์ดํŠธ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋Œ์•„์˜จ๋‹ค.
#dreamhack#ctf#reversing+5
2026-08-17#dreamhack +5
[๐Ÿฅˆ Silver 1] ํ‚ค์ŠคํŠธ๋ฆผ 1๋น„ํŠธ๋กœ ๋˜๋Œ๋ฆฐ SNOW 3G โ€” DreamHack snow ํ’€์ด
blog

[๐Ÿฅˆ Silver 1] ํ‚ค์ŠคํŠธ๋ฆผ 1๋น„ํŠธ๋กœ ๋˜๋Œ๋ฆฐ SNOW 3G โ€” DreamHack snow ํ’€์ด

20KB ์งœ๋ฆฌ ์ŠคํŠธ๋ฆฝํŠธ ELF ์•ˆ์— 3GPP ํ‘œ์ค€ ์ŠคํŠธ๋ฆผ ์•”ํ˜ธ SNOW 3G ๊ฐ€ ํ†ต์งธ๋กœ ๋“ค์–ด ์žˆ์—ˆ๋‹ค. ํ‚ค๋Š” "Merry-Christmas!" ๋กœ ๊ณ ์ •์ด๊ณ  IV ๋Š” ์šฐ๋ฆฌ๊ฐ€ ๋„ฃ๋Š” ์ž…๋ ฅ์˜ ์•ž 16๋ฐ”์ดํŠธ โ€” ์ฆ‰ ๋ณต์›ํ•ด์•ผ ํ•  ๊ฐ’์ด ์ž๊ธฐ ์ž์‹ ์„ ์•”ํ˜ธํ™”ํ•œ๋‹ค. ์›Œ๋“œ๋งˆ๋‹ค ํ•„์š”ํ•œ ์ •๋ณด๊ฐ€ ํ‚ค์ŠคํŠธ๋ฆผ์˜ ์ตœํ•˜์œ„ 1๋น„ํŠธ๋ฟ์ด๋ผ๋Š” ์ ์„ ์ด์šฉํ•ด 256๊ฐ€์ง€๋งŒ ํ™•์ธํ•˜๊ณ  32๊ธ€์ž๋ฅผ ๋˜๋Œ๋ ธ๋‹ค.
#dreamhack#ctf#reversing+6
2026-08-17#dreamhack +5