[๐Ÿฅ‡ Gold 1] ๋ธŒ๋ผ์šฐ์ € ์•ˆ wasm ํ•œ ๋ฉ์–ด๋ฆฌ ๋˜๋Œ๋ฆฌ๊ธฐ โ€” DreamHack passcode ํ’€์ด

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

[๐Ÿฅ‡ Gold 1] ๋ธŒ๋ผ์šฐ์ € ์•ˆ wasm ํ•œ ๋ฉ์–ด๋ฆฌ ๋˜๋Œ๋ฆฌ๊ธฐ โ€” DreamHack passcode ํ’€์ด

HTML ํ•œ ์žฅ๊ณผ JavaScript ํ•œ ์žฅ์ด ์ „๋ถ€์ธ ๋ฌธ์ œ. main.js ์ฒซ ์ค„์— ๋ฐ•ํžŒ 1,984๋ฐ”์ดํŠธ WebAssembly ๋ชจ๋“ˆ์ด 25๊ธ€์ž passcode๋ฅผ ๊ฒ€์‚ฌํ•œ๋‹ค. wabt๋กœ ๋œฏ์–ด ๋ณด๋‹ˆ 256ํšŒ ๋ฃจํ”„๊ฐ€ ๋ฐ”์ดํŠธ๋ฅผ ์ „ํ˜€ ์„ž์ง€ ์•Š์•„, 25๊ฐœ ์œ„์น˜๊ฐ€ ๊ฐ์ž ๋…๋ฆฝ๋œ ์น˜ํ™˜ ์‚ฌ์Šฌ์„ ํƒ„๋‹ค. ์œ„์น˜๋งˆ๋‹ค 256๊ฐœ ํ›„๋ณด๋ฅผ ์ •๋ฐฉํ–ฅ์œผ๋กœ ๋Œ๋ ค ์—ญ์ƒ์„ ํ•˜๋‚˜์”ฉ ํ™•์ •ํ–ˆ๋‹ค.

๋ฌธ์ œ: DreamHack โ€” passcode ๋ถ„๋ฅ˜: reversing ๋‚œ์ด๋„: ๐Ÿฅ‡ Gold 1 FLAG: DH{W3B@sSemb1y_R3ver5in9_^-^}

๋ฌธ์ œ ์„ค๋ช…์€ ํ•œ ์ค„์ด๋‹ค. "์˜ฌ๋ฐ”๋ฅธ passcode๋ฅผ ์ฐพ์•„ DH{} ํฌ๋งท์— ๋„ฃ์–ด์„œ ์ธ์ฆํ•ด์ฃผ์„ธ์š”!"

๋ฐ›์€ ์••์ถ•์„ ํ’€๋ฉด ํŒŒ์ผ์ด ๋‘˜๋ฟ์ด๋‹ค. ์„œ๋ฒ„๋„ ์—†๊ณ  ELF๋„ ์—†๋‹ค.

ls -l extracted && file extracted/index.html extracted/main.js && wc -c extracted/main.js

ls -l ๊ณผ file ๋กœ ํ™•์ธํ•œ ๋ฐฐํฌ๋ณธ โ€” index.html 1654๋ฐ”์ดํŠธ์™€ main.js 9038๋ฐ”์ดํŠธ ๋‘˜๋ฟ์ด๊ณ , main.js ๋Š” 8051์ž์งœ๋ฆฌ ํ•œ ์ค„์ด ๋“ค์–ด ์žˆ๋Š” JavaScript ์†Œ์Šค๋กœ ์žกํžŒ๋‹ค
ls -l ๊ณผ file ๋กœ ํ™•์ธํ•œ ๋ฐฐํฌ๋ณธ โ€” index.html 1654๋ฐ”์ดํŠธ์™€ main.js 9038๋ฐ”์ดํŠธ ๋‘˜๋ฟ์ด๊ณ , main.js ๋Š” 8051์ž์งœ๋ฆฌ ํ•œ ์ค„์ด ๋“ค์–ด ์žˆ๋Š” JavaScript ์†Œ์Šค๋กœ ์žกํžŒ๋‹ค

main.js๊ฐ€ 9,038๋ฐ”์ดํŠธ์ธ๋ฐ ๊ทธ์ค‘ 8,051์ž๊ฐ€ ํ•œ ์ค„์ด๋‹ค. ์—ด์–ด ๋ณด๋ฉด ๊ทธ ํ•œ ์ค„์ด ๋ญ”์ง€ ๋ฐ”๋กœ ๋‚˜์˜จ๋‹ค.


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…passcode
๋‚œ์ด๋„๐Ÿฅ‡ Gold 1
๋ถ„๋ฅ˜reversing
์ œ๊ณต ํŒŒ์ผindex.html, main.js (์„œ๋ฒ„ ์—†์Œ, ์™„์ „ ์˜คํ”„๋ผ์ธ)
์Šคํƒ๋ธŒ๋ผ์šฐ์ € ยท WebAssembly MVP ยท jQuery ยท SweetAlert2 ยท Bulma
ํ•ต์‹ฌ ์ทจ์•ฝ์  / ๊ธฐ๋ฒ•๋ฐ”์ดํŠธ๋ฅผ ์„ž์ง€ ์•Š๋Š” ์น˜ํ™˜ ์‚ฌ์Šฌ โ€” ์œ„์น˜๋ณ„ ์ „์ˆ˜ ์—ญ์‚ฐ

ํ’€์ด ํ๋ฆ„์€ ์ด๋ ‡๋‹ค. main.js์— ๋ฐ•ํžŒ ๋ฐ”์ดํŠธ ๋ฐฐ์—ด์„ ํŒŒ์ผ๋กœ ๋–ผ์–ด ๋‚ด wasm ๋ชจ๋“ˆ๋กœ ๋งŒ๋“ค๊ณ , wabt๋กœ ๋””์ปดํŒŒ์ผํ•ด ๊ฒ€์‚ฌ ํ•จ์ˆ˜ f()๋ฅผ ์ฝ๋Š”๋‹ค. ๋ฃจํ”„๊ฐ€ ๋ฐ”์ดํŠธ๋ฅผ ์„ž์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฑธ ํ™•์ธํ•œ ๋‹ค์Œ, ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ƒ์ˆ˜ ํ…Œ์ด๋ธ” ์„ธ ๊ฐœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋– ๋‚ด๊ณ , ์œ„์น˜๋งˆ๋‹ค 256๊ฐœ ํ›„๋ณด๋ฅผ ์ •๋ฐฉํ–ฅ์œผ๋กœ ๋Œ๋ ค ๋‹ต์„ ํ™•์ •ํ•œ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ์ง„์งœ wasm๊ณผ ์ง„์งœ ๋ธŒ๋ผ์šฐ์ €์— ๋จน์—ฌ ๋งž๋Š”์ง€ ๋ณธ๋‹ค.



๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” ๋ธŒ๋ผ์šฐ์ €์—์„œ wasm์ด ๋„๋Š” ๊ฐ€์žฅ ์ž‘์€ ํ˜•ํƒœ

main.js๋Š” ์„ธ ๋ถ€๋ถ„์ด๋‹ค. ์ฒซ ์ค„์ด ๋ฐ”์ดํŠธ ๋ฐฐ์—ด, ๊ทธ๋‹ค์Œ์ด ์ธ์Šคํ„ด์Šคํ™”, ๋‚˜๋จธ์ง€๊ฐ€ ํผ ํ•ธ๋“ค๋Ÿฌ.

code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 17, 3, 96, 0, 0, /* ... 1984๊ฐœ ... */]);
WebAssembly.instantiate(code).then(result => {
    instance = result.instance;
});

0, 97, 115, 109๋Š” \0asm์ด๋‹ค. wasm ๋งค์ง ๋„˜๋ฒ„๋ฅผ ์‹ญ์ง„์ˆ˜๋กœ ๋Š˜์–ด๋†“์€ ๊ฒƒ๋ฟ์ด๋ผ, ์ด ๋ฐฐ์—ด์ด ๊ณง ํ•˜๋‚˜์˜ wasm ๋ชจ๋“ˆ ํŒŒ์ผ์ด๋‹ค. ๋„คํŠธ์›Œํฌ๋กœ .wasm์„ ๋ฐ›์•„ ์˜ค์ง€ ์•Š๊ณ  JS ์†Œ์Šค ์•ˆ์— ํ†ต์งธ๋กœ ๋„ฃ์–ด ๋’€๋‹ค.

๊ฒ€์‚ฌ๋Š” ํผ submit์—์„œ ์ผ์–ด๋‚œ๋‹ค.

$('#passcode-form').on('submit', function(e) {
    e.preventDefault();
    passcode = $('#passcode').val();
    array = new Uint8Array(instance.exports.memory.buffer, 0, passcode.length)
    array.set(passcode.split('').map(x => x.charCodeAt()));
    result = instance.exports.f(array.byteOffset, array.length);
    if (!result) { /* Swal: Correct! */ } else { /* Swal: Wrong! */ }
});

์—ฌ๊ธฐ์„œ ๋ˆˆ์—ฌ๊ฒจ๋ณผ ๊ฒŒ ๋‘ ๊ฐœ๋‹ค.

์ž…๋ ฅ์ด ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ 0๋ฒˆ์ง€์— ๊ทธ๋Œ€๋กœ ์จ์ง„๋‹ค. wasm ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ๋Š” ๊ทธ๋ƒฅ ํ•˜๋‚˜์˜ ArrayBuffer๋ผ JS ์ชฝ์—์„œ ์•„๋ฌด ์˜คํ”„์…‹์ด๋‚˜ ์žก์•„ ์“ธ ์ˆ˜ ์žˆ๊ณ , ์ด ํŽ˜์ด์ง€๋Š” 0๋ฒˆ์ง€๋ฅผ ๊ณจ๋ž๋‹ค. ๊ทธ๋ฆฌ๊ณ  f()์— ๋„˜๊ธฐ๋Š” ๊ฑด ๊ทธ ์ฃผ์†Œ(byteOffset = 0)์™€ ๊ธธ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  ํŒ์ •์ด if (!result)๋‹ค. f()๊ฐ€ 0์„ ๋Œ๋ ค์ฃผ๋ฉด ์ •๋‹ต์ด๋‹ค. C์˜ memcmp ๊ด€๋ก€๊ฐ€ ๊ทธ๋Œ€๋กœ ํ™”๋ฉด๊นŒ์ง€ ์˜ฌ๋ผ์˜จ๋‹ค.

๋จผ์ € ๋ฐฐ์—ด์„ ํŒŒ์ผ๋กœ ๋–ผ์–ด ๋ƒˆ๋‹ค. ์†์œผ๋กœ ์„ธ๋Š” ๋Œ€์‹  ์ •๊ทœ์‹ ํ•œ ์ค„๋กœ ๊ธ๋Š”๋‹ค.

#!/usr/bin/env python3
"""main.js 1ํ–‰์˜ Uint8Array ๋ฆฌํ„ฐ๋Ÿด์„ ๊ทธ๋Œ€๋กœ ๊บผ๋‚ด passcode.wasm ์œผ๋กœ ์ €์žฅํ•œ๋‹ค."""
import re, pathlib
 
src = pathlib.Path("extracted/main.js").read_text()
m = re.search(r"new Uint8Array\(\[(.*?)\]\)", src, re.S)
nums = [int(x) for x in m.group(1).split(",")]
blob = bytes(nums)
pathlib.Path("passcode.wasm").write_bytes(blob)
print(f"bytes={len(blob)} magic={blob[:4].hex()} version={int.from_bytes(blob[4:8],'little')}")

wabt๋Š” ์‹œ์Šคํ…œ์— ์—†์–ด์„œ npm์œผ๋กœ ๋ฐ›์•˜๋‹ค(npm i wabt). ์ด ํŒจํ‚ค์ง€๋Š” JS ๋ฐ”์ธ๋”ฉ๋ฟ ์•„๋‹ˆ๋ผ node_modules/wabt/bin/ ์•„๋ž˜์— wasm-objdumpยทwasm-decompileยทwasm-validate ์‹คํ–‰ํŒŒ์ผ๊นŒ์ง€ ๊ฐ™์ด ๊น”์•„ ์ค€๋‹ค.

python3 extract_wasm.py && file passcode.wasm && ./node_modules/wabt/bin/wasm-validate passcode.wasm && echo "wasm-validate: OK"

extract_wasm.py ๋กœ ๋ฝ‘์•„๋‚ธ passcode.wasm 1984๋ฐ”์ดํŠธ โ€” file ์€ WebAssembly binary module version 0x1 ๋กœ ๋ณด๊ณ ํ•˜๊ณ , wabt ์˜ wasm-validate ๋„ ์ด์˜ ์—†์ด OK ๋กœ ๋‹ตํ•œ๋‹ค
extract_wasm.py ๋กœ ๋ฝ‘์•„๋‚ธ passcode.wasm 1984๋ฐ”์ดํŠธ โ€” file ์€ WebAssembly binary module version 0x1 ๋กœ ๋ณด๊ณ ํ•˜๊ณ , wabt ์˜ wasm-validate ๋„ ์ด์˜ ์—†์ด OK ๋กœ ๋‹ตํ•œ๋‹ค

1,984๋ฐ”์ดํŠธ. ๊ฒ€์ฆ๋„ ํ†ต๊ณผํ•œ๋‹ค. ์ด์ œ๋ถ€ํ„ฐ๋Š” ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์žŠ๊ณ  ๋ฐ”์ด๋„ˆ๋ฆฌ ํ•˜๋‚˜๋งŒ ๋ณธ๋‹ค.


๐Ÿ”ฌ ์ •์ฐฐ โ€” ์„น์…˜๊ณผ export๋ถ€ํ„ฐ

wasm-objdump -h๋กœ ์„น์…˜ ๋ฐฐ์น˜๋ฅผ ๋ณธ๋‹ค.

./node_modules/wabt/bin/wasm-objdump -h passcode.wasm

wasm-objdump -h ๋กœ ๋ณธ ์„น์…˜ ๋ฐฐ์น˜ โ€” Code ๊ฐ€ 0x22b, Data ๊ฐ€ 0x228 ๋ฐ”์ดํŠธ์ด๊ณ  ๊ทธ ๋’ค๋กœ .debug_info ยท .debug_line ยท .debug_str ์ปค์Šคํ…€ ์„น์…˜์ด ํ†ต์งธ๋กœ ๋‚จ์•„ ์žˆ๋‹ค
wasm-objdump -h ๋กœ ๋ณธ ์„น์…˜ ๋ฐฐ์น˜ โ€” Code ๊ฐ€ 0x22b, Data ๊ฐ€ 0x228 ๋ฐ”์ดํŠธ์ด๊ณ  ๊ทธ ๋’ค๋กœ .debug_info ยท .debug_line ยท .debug_str ์ปค์Šคํ…€ ์„น์…˜์ด ํ†ต์งธ๋กœ ๋‚จ์•„ ์žˆ๋‹ค

Code๊ฐ€ 0x22b(555๋ฐ”์ดํŠธ), Data๊ฐ€ 0x228(552๋ฐ”์ดํŠธ). ์ฝ”๋“œ๋ณด๋‹ค ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฑฐ์˜ ๊ฐ™์€ ๋ฉ์น˜๋ผ๋Š” ๊ฒŒ ์ฒซ ํžŒํŠธ๋‹ค. ์ƒ์ˆ˜ ํ…Œ์ด๋ธ”์ด ํฌ๋‹ค๋Š” ๋œป์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋””๋ฒ„๊ทธ ์„น์…˜์ด ์ŠคํŠธ๋ฆฝ๋˜์ง€ ์•Š์•˜๋‹ค. .debug_str์„ ํ›‘์œผ๋ฉด ๋นŒ๋“œ ํ™˜๊ฒฝ์ด ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค.

strings -a -n 8 passcode.wasm | grep -iE "musl|clang|wasmception|memcmp"
/home/yury/Work/wasmception/src/musl/src/string
memcmp.c
.debug_strclang version 8.0.0 (trunk 341960)
/home/yury/Work/wasmception/src/musl/src/string/memcmp.c
/home/yury/Work/wasmception/build/musl

wasmception์ด๋ผ๋Š” ์ดˆ๊ธฐ standalone wasm ํˆด์ฒด์ธ์œผ๋กœ, clang 8.0.0 ํŠธ๋ ํฌ๋กœ ๋นŒ๋“œํ–ˆ๊ณ , musl์˜ memcmp.c๊ฐ€ ๊ทธ๋Œ€๋กœ ๋งํฌ๋๋‹ค. ํ’€์ด์— ๊ผญ ํ•„์š”ํ•œ ์ •๋ณด๋Š” ์•„๋‹ˆ์ง€๋งŒ "์ด ์•ˆ์— memcmp๊ฐ€ ์ง„์งœ๋กœ ๋“ค์–ด ์žˆ๋‹ค"๋Š” ๊ฑธ ๋ฏธ๋ฆฌ ์•Œ๋ ค ์ค€๋‹ค.

๋‚ด๋ณด๋‚ด๋Š” ์‹ฌ๋ณผ์€ ๋„ท๋ฟ์ด๋‹ค.

./node_modules/wabt/bin/wasm-objdump -x -j Export passcode.wasm

wasm-objdump -x -j Export ๊ฒฐ๊ณผ โ€” ๋‚ด๋ณด๋‚ด๋Š” ๊ฒƒ์€ memory, __heap_base, __data_end, ๊ทธ๋ฆฌ๊ณ  func[1] ์ธ f ๋„ท๋ฟ์ด๊ณ  import ์„น์…˜์€ ์•„์˜ˆ ์—†๋‹ค
wasm-objdump -x -j Export ๊ฒฐ๊ณผ โ€” ๋‚ด๋ณด๋‚ด๋Š” ๊ฒƒ์€ memory, __heap_base, __data_end, ๊ทธ๋ฆฌ๊ณ  func[1] ์ธ f ๋„ท๋ฟ์ด๊ณ  import ์„น์…˜์€ ์•„์˜ˆ ์—†๋‹ค

๊ทธ๋ฆฌ๊ณ  import ์„น์…˜์ด ์•„์˜ˆ ์—†๋‹ค. ๋ฐ”๊นฅ์—์„œ ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๋ฐ›๋Š”๋‹ค๋Š” ๋œป์ด๋ผ, f()์˜ ํŒ์ •์€ ์ˆœ์ „ํžˆ ์ž๊ธฐ ์•ˆ์˜ ์ƒ์ˆ˜๋งŒ์œผ๋กœ ์ด๋ค„์ง„๋‹ค. ์„œ๋ฒ„๊ฐ€ ์—†๋Š” ๋ฌธ์ œ์ด๋‹ˆ ๋‹น์—ฐํ•˜์ง€๋งŒ, "๋„คํŠธ์›Œํฌ๋กœ ์ •๋‹ต์„ ๋ฌผ์–ด๋ณด๋Š” ๊ตฌ์กฐ๊ฐ€ ์•„๋‹ˆ๋‹ค"๋ฅผ ํ™•์ธํ•˜๊ณ  ๊ฐ€๋Š” ๊ฒŒ ๋‚ซ๋‹ค.

__data_end๊ฐ€ 1568์ด๋ผ๋Š” ๊ฒƒ๋„ ์—ฌ๊ธฐ์„œ ์–ป๋Š”๋‹ค. ์ƒ์ˆ˜ ์˜์—ญ์ด ์–ด๋””์„œ ๋๋‚˜๋Š”์ง€ ์•Œ๋ ค ์ฃผ๋Š” ๊ฐ’์ด๋‹ค.



๐Ÿ’ฃ f() ๋œฏ์–ด๋ณด๊ธฐ

WAT๋ฅผ ๋ฐ”๋กœ ์ฝ์–ด๋„ ๋˜์ง€๋งŒ, wasm-decompile์ด C ๋น„์Šทํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ๋ฝ‘์•„ ์ฃผ๋Š” ๊ฒŒ ํ›จ์”ฌ ๋น ๋ฅด๋‹ค.

./node_modules/wabt/bin/wasm-decompile passcode.wasm -o passcode.dcmp && sed -n "32,52p" passcode.dcmp

wasm-decompile ์ด ๋ฝ‘์€ f() ์˜์‚ฌ์ฝ”๋“œ โ€” ๊ธธ์ด 25 ๊ฒ€์‚ฌ, 256ํšŒ ๋ฃจํ”„, (c and 255) % 25 ์ธ๋ฑ์‹ฑ, 1056 XOR, ํšŒ์ „, 1312 ์กฐํšŒ๊ฐ€ ์Šค๋ฌด ์ค„ ์•ˆ์— ๋‹ค ๋ณด์ธ๋‹ค
wasm-decompile ์ด ๋ฝ‘์€ f() ์˜์‚ฌ์ฝ”๋“œ โ€” ๊ธธ์ด 25 ๊ฒ€์‚ฌ, 256ํšŒ ๋ฃจํ”„, (c and 255) % 25 ์ธ๋ฑ์‹ฑ, 1056 XOR, ํšŒ์ „, 1312 ์กฐํšŒ๊ฐ€ ์Šค๋ฌด ์ค„ ์•ˆ์— ๋‹ค ๋ณด์ธ๋‹ค

์Šค๋ฌด ์ค„์ด๋‹ค. ์˜ฎ๊ฒจ ์ ์œผ๋ฉด ์ด๋ ‡๋‹ค.

export function f(a:int, b:int):int {     // a = ๋ฒ„ํผ ์ฃผ์†Œ, b = ๊ธธ์ด
  var d:int;
  var c:int = 255;
  if (b != 25) goto B_a;                  // ๊ธธ์ด๊ฐ€ 25๊ฐ€ ์•„๋‹ˆ๋ฉด ์ฆ‰์‹œ 255 ๋ฐ˜ํ™˜
  c = 0;
  loop L_b {
    b = a + (c & 255) % 25;               // ๊ฑด๋“œ๋ฆด ๋ฐ”์ดํŠธ ์ฃผ์†Œ
    b[0]:byte = (d = b[0]:ubyte ^ (c + 1056)[0]:ubyte);
    var e:int = c & 7;
    var f:int = 0 - c & 7;
    if (c & 1) goto B_d;
    d = (d & 255) >> e | d << f;          // c ๊ฐ€ ์ง์ˆ˜: ์˜ค๋ฅธ์ชฝ์œผ๋กœ e ๋งŒํผ ํšŒ์ „
    goto B_c;
    label B_d:
    d = d << e | (d & 255) >> f;          // c ๊ฐ€ ํ™€์ˆ˜: ์™ผ์ชฝ์œผ๋กœ e ๋งŒํผ ํšŒ์ „
    label B_c:
    b[0]:byte = d;
    b[0]:byte = ((d & 255) + 1312)[0]:ubyte;   // sbox ์น˜ํ™˜
    c = c + 1;
    if (c != 256) continue L_b;
  }
  /* ... buf ๋’ค์ง‘๊ธฐ 12์Œ ... */
  c = memcmp(a, 1024, 25);
  label B_a:
  return c & 255;
}

ํ•ต์‹ฌ์€ ๋‘ ๋ฒˆ์งธ ์ค„, b = a + (c & 255) % 25 ๋‹ค. ๋ฃจํ”„ ๋ณ€์ˆ˜ c๊ฐ€ 0๋ถ€ํ„ฐ 255๊นŒ์ง€ ๋„๋Š”๋ฐ, ๋งค ๋ฐ˜๋ณต์ด ๊ฑด๋“œ๋ฆฌ๋Š” ๋ฐ”์ดํŠธ๋Š” c % 25๋ฒˆ์งธ ํ•˜๋‚˜๋ฟ์ด๋‹ค.

WAT ์›๋ฌธ์—์„œ๋„ ๊ฐ™์€ ์ž๋ฆฌ๋ฅผ ์งš์–ด ๋‘์ž. sed๋กœ ์ฃผ์„๋งŒ ์–น์—ˆ๋‹ค. ํ™”์‚ดํ‘œ ์ฃผ์„์€ ๋‚ด๊ฐ€ ๋ถ™์ธ ๊ฒƒ์ด๊ณ , ๋‚˜๋จธ์ง€๋Š” wasm2wat ์ถœ๋ ฅ ๊ทธ๋Œ€๋กœ๋‹ค.

s|i32.rem_u$|i32.rem_u                <-- j = (i \& 0xff) % 25  โ†’  ๊ฑด๋“œ๋ฆด ๋ฐ”์ดํŠธ ์œ„์น˜|
s|i32.const 1056$|i32.const 1056           <-- key ํ…Œ์ด๋ธ” ์‹œ์ž‘ ์ฃผ์†Œ|
s|i32.const 1312$|i32.const 1312           <-- sbox ํ…Œ์ด๋ธ” ์‹œ์ž‘ ์ฃผ์†Œ|
s|i32.xor$|i32.xor                  <-- v = buf[j] ^ key[i]|
s|i32.const 256$|i32.const 256            <-- ๋ฃจํ”„๋Š” 256 ํšŒ|
s|i32.const 7$|i32.const 7              <-- ํšŒ์ „๋Ÿ‰์€ ํ•˜์œ„ 3๋น„ํŠธ๋งŒ|

i32.const 25์™€ i32.const 255๊ฐ€ ๋‘˜ ๋‹ค ๋‚˜์˜ค๊ธฐ ๋•Œ๋ฌธ์— $ ์•ต์ปค๊ฐ€ ์—†์œผ๋ฉด 255์˜ ์•ž๋ถ€๋ถ„์ด ์ž˜๋ชป ์žกํžŒ๋‹ค. ์ด sed ํŒŒ์ผ์€ annotate_loop.sed๋กœ ๋ฌธ์ œ ํด๋”์— ๋‚จ๊ฒจ ๋’€๋‹ค.

WAT๋Š” wabt์˜ JS ๋ฐ”์ธ๋”ฉ์œผ๋กœ ๋ฝ‘์•˜๋‹ค. wasm2wat ์‹คํ–‰ํŒŒ์ผ๋„ ์žˆ์ง€๋งŒ, ์ด๋ฆ„ ์„น์…˜์„ ๋ถ™์—ฌ ํ•จ์ˆ˜๋ช…์„ $f๋กœ ์‚ด๋ฆฌ๋ ค๊ณ  ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋”ฐ๋กœ ์ผ๋‹ค.

// wabt(npm) ๋กœ passcode.wasm ์„ WAT ํ…์ŠคํŠธ๋กœ ํ’€์–ด ์“ด๋‹ค. wasm2wat ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์—†๋Š” ํ™˜๊ฒฝ์šฉ.
const fs = require('fs');
require('wabt')().then(wabt => {
  const buf = fs.readFileSync(process.argv[2] || 'passcode.wasm');
  const mod = wabt.readWasm(new Uint8Array(buf), { readDebugNames: true });
  mod.applyNames();
  fs.writeFileSync('passcode.wat', mod.toText({ foldExprs: false, inlineExport: false }));
  console.log('WAT written: passcode.wat');
});
node wasm2wat.js && sed -n "17,50p" passcode.wat | sed -f annotate_loop.sed

wasm2wat ๋กœ ํ‘ผ WAT ์— sed ๋กœ ์ฃผ์„์„ ์–น์€ ํ•ต์‹ฌ ๋ฃจํ”„ โ€” i32.rem_u ๊ฐ€ ๋ฒ„ํผ ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“ค๊ณ  i32.const 1056 ์ด key ํ…Œ์ด๋ธ” ์ฃผ์†Œ๋ฅผ, i32.const 7 ์ด ํšŒ์ „๋Ÿ‰์„ ๋งŒ๋“ ๋‹ค
wasm2wat ๋กœ ํ‘ผ WAT ์— sed ๋กœ ์ฃผ์„์„ ์–น์€ ํ•ต์‹ฌ ๋ฃจํ”„ โ€” i32.rem_u ๊ฐ€ ๋ฒ„ํผ ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“ค๊ณ  i32.const 1056 ์ด key ํ…Œ์ด๋ธ” ์ฃผ์†Œ๋ฅผ, i32.const 7 ์ด ํšŒ์ „๋Ÿ‰์„ ๋งŒ๋“ ๋‹ค

ํ•œ ๋ฐ˜๋ณต์—์„œ ์ผ์–ด๋‚˜๋Š” ์ผ์„ ์ •๋ฆฌํ•˜๋ฉด ์„ธ ๋‹จ๊ณ„๋‹ค.

ํ•œ ๋ฐ”์ดํŠธ์— ๊ฐ€ํ•ด์ง€๋Š” ์„ธ ์—ฐ์‚ฐ โ€” key XOR, ํ•˜์œ„ 3๋น„ํŠธ๋งŒํผ์˜ 8๋น„ํŠธ ํšŒ์ „, sbox ์น˜ํ™˜. ์˜† ๋ฐ”์ดํŠธ๋ฅผ ์ฝ์ง€๋„ ์“ฐ์ง€๋„ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์Šฌ ์ „์ฒด๊ฐ€ ์ „๋‹จ์‚ฌ๊ฐ€ ๋œ๋‹ค
ํ•œ ๋ฐ”์ดํŠธ์— ๊ฐ€ํ•ด์ง€๋Š” ์„ธ ์—ฐ์‚ฐ โ€” key XOR, ํ•˜์œ„ 3๋น„ํŠธ๋งŒํผ์˜ 8๋น„ํŠธ ํšŒ์ „, sbox ์น˜ํ™˜. ์˜† ๋ฐ”์ดํŠธ๋ฅผ ์ฝ์ง€๋„ ์“ฐ์ง€๋„ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์Šฌ ์ „์ฒด๊ฐ€ ์ „๋‹จ์‚ฌ๊ฐ€ ๋œ๋‹ค

ํšŒ์ „ ๋ฐฉํ–ฅ์ด c์˜ ํ™€์ง์œผ๋กœ ๊ฐˆ๋ฆฐ๋‹ค. e = c & 7, f = (0 - c) & 7์ด๋ผ e + f๊ฐ€ ํ•ญ์ƒ 8์ด๊ณ , ์ง์ˆ˜ ๋ฐ˜๋ณต์€ ์˜ค๋ฅธ์ชฝ์œผ๋กœ e๋งŒํผ, ํ™€์ˆ˜ ๋ฐ˜๋ณต์€ ์™ผ์ชฝ์œผ๋กœ e๋งŒํผ ๋„๋Š” ์ •ํ™•ํ•œ 8๋น„ํŠธ ํšŒ์ „์ด๋‹ค. XOR ์งํ›„ ๊ฐ’์ด 0~255 ๋ฒ”์œ„๋ผ & 255 ๋งˆ์Šคํฌ์™€ ๋งž๋ฌผ๋ ค ์ƒ์œ„ ๋น„ํŠธ๊ฐ€ ์ƒˆ์ง€ ์•Š๋Š”๋‹ค.

์—ฌ๊ธฐ์„œ ๊ฒฐ์ •์ ์ธ ๊ฒŒ ํ•˜๋‚˜ ๋‚˜์˜จ๋‹ค. ์„ธ ์—ฐ์‚ฐ ์ „๋ถ€ ๋ฐ”์ดํŠธ ํ•˜๋‚˜ ์•ˆ์—์„œ ๋๋‚œ๋‹ค. ์˜† ๋ฐ”์ดํŠธ๋ฅผ ์ฝ์ง€๋„, ์“ฐ์ง€๋„ ์•Š๋Š”๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ 25๋ฐ”์ดํŠธ๋ฅผ ์—ฐ๋ฆฝ๋ฐฉ์ •์‹์œผ๋กœ ๋ฌถ์–ด ํ’€ ํ•„์š”๊ฐ€ ์—†๋‹ค.

256ํšŒ ๋ฃจํ”„๊ฐ€ j = i % 25 ๋กœ 25๊ฐœ ๋…๋ฆฝ ์‚ฌ์Šฌ๋กœ ๊ฐˆ๋ผ์ง€๋Š” ๊ตฌ์กฐ์™€, ๋ฃจํ”„๊ฐ€ ๋๋‚œ ๋’ค buf ๋’ค์ง‘๊ธฐ์™€ memcmp 1024 ๋น„๊ต๋กœ ์ด์–ด์ง€๋Š” ํ๋ฆ„
256ํšŒ ๋ฃจํ”„๊ฐ€ j = i % 25 ๋กœ 25๊ฐœ ๋…๋ฆฝ ์‚ฌ์Šฌ๋กœ ๊ฐˆ๋ผ์ง€๋Š” ๊ตฌ์กฐ์™€, ๋ฃจํ”„๊ฐ€ ๋๋‚œ ๋’ค buf ๋’ค์ง‘๊ธฐ์™€ memcmp 1024 ๋น„๊ต๋กœ ์ด์–ด์ง€๋Š” ํ๋ฆ„

i๊ฐ€ 0..255๋ฅผ ๋„๋‹ˆ ์œ„์น˜ 0~4๋Š” 11๋ฒˆ, 5๋„ 11๋ฒˆ, ๋‚˜๋จธ์ง€๋Š” 10๋ฒˆ์”ฉ ์น˜ํ™˜์„ ๋ฐ›๋Š”๋‹ค. ์œ„์น˜๋งˆ๋‹ค ํšŒ์ „๋Ÿ‰๋„ ์ˆœ์„œ๋„ ๋‹ค๋ฅด์ง€๋งŒ, ์–ด์ฐจํ”ผ ์„œ๋กœ ๋…๋ฆฝ์ด๋ผ ์ƒ๊ด€์—†๋‹ค.

๋ฃจํ”„๊ฐ€ ๋๋‚œ ๋’ค์—๋Š” ์—ด๋‘ ์Œ์„ ๋งž๋ฐ”๊ฟ” ๋ฒ„ํผ๋ฅผ ํ†ต์งธ๋กœ ๋’ค์ง‘๋Š”๋‹ค(buf[0]โ†”buf[24], buf[1]โ†”buf[23], โ€ฆ, buf[11]โ†”buf[13]). ๊ฐ€์šด๋ฐ buf[12]๋งŒ ์ œ์ž๋ฆฌ๋‹ค. ๊ทธ๋ฆฌ๊ณ  memcmp(buf, 1024, 25).

๋ฐ˜ํ™˜๊ฐ’์€ c & 255๋‹ค. musl memcmp๋Š” ์ฒ˜์Œ ๋‹ค๋ฅธ ๋ฐ”์ดํŠธ์˜ ์ฐจ๋ฅผ ๋Œ๋ ค์ฃผ๋Š”๋ฐ, ๋‘ ๊ฐ’ ๋ชจ๋‘ 0~255๋ผ ์ฐจ๊ฐ€ ์ •ํ™•ํžˆ 0์ผ ๋•Œ๋งŒ & 255๊ฐ€ 0์ด ๋œ๋‹ค. ์šฐ์—ฐํžˆ 0์œผ๋กœ ์ ‘ํžˆ๋Š” ๊ฒฝ์šฐ๋Š” ์—†๋‹ค.



๐Ÿงฑ ๋ฐ์ดํ„ฐ ์„น์…˜ โ€” ์ƒ์ˆ˜ ์„ธ ๋ฉ์–ด๋ฆฌ

์ฃผ์†Œ ์„ธ ๊ฐœ๊ฐ€ ๋‚˜์™”๋‹ค. 1024(๋น„๊ต ๋Œ€์ƒ), 1056(key), 1312(sbox). __data_end๋Š” 1568.

๊ฐ„๊ฒฉ์„ ๋ณด๋ฉด ๋ฐฐ์น˜๊ฐ€ ์ฝํžŒ๋‹ค. 1024๋ถ€ํ„ฐ 25๋ฐ”์ดํŠธ๊ฐ€ target, 1056๊นŒ์ง€ 7๋ฐ”์ดํŠธ๋Š” 0 ํŒจ๋”ฉ, 1056๋ถ€ํ„ฐ 256๋ฐ”์ดํŠธ๊ฐ€ key, 1312๋ถ€ํ„ฐ 256๋ฐ”์ดํŠธ๊ฐ€ sbox, ๊ทธ๋ž˜์„œ 1312 + 256 = 1568. ๋”ฑ ๋งž๋Š”๋‹ค.

LEB128์„ ์†์œผ๋กœ ์„ธ๋Š” ๋Œ€์‹  wasm์„ ์‹ค์ œ๋กœ ์ธ์Šคํ„ด์Šคํ™”ํ•ด์„œ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ทธ๋Œ€๋กœ ๋– ๋ƒˆ๋‹ค. ์˜ค๋…์˜ ์—ฌ์ง€๊ฐ€ ์—†๋‹ค.

// wasm ์„ ์‹ค์ œ๋กœ ์ธ์Šคํ„ด์Šคํ™”ํ•ด ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ƒ์ˆ˜ ํ…Œ์ด๋ธ” 3๊ฐœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋– ๋‚ธ๋‹ค.
// ์†์œผ๋กœ LEB128 ์„ ์„ธ๋Š” ๊ฒƒ๋ณด๋‹ค ์ด์ชฝ์ด ์˜ค๋…์˜ ์—ฌ์ง€๊ฐ€ ์—†๋‹ค.
const fs = require('fs');
const buf = fs.readFileSync('passcode.wasm');
WebAssembly.instantiate(new Uint8Array(buf)).then(({ instance }) => {
  const mem = new Uint8Array(instance.exports.memory.buffer);
  const target = Array.from(mem.slice(1024, 1024 + 25));
  const key    = Array.from(mem.slice(1056, 1056 + 256));
  const sbox   = Array.from(mem.slice(1312, 1312 + 256));
  console.log('target[25] =', JSON.stringify(target));
  console.log('sbox ๊ฐ€ ์ˆœ์—ด์ธ๊ฐ€:', new Set(sbox).size === 256);
  console.log('key  ๊ฐ€ ์ˆœ์—ด์ธ๊ฐ€:', new Set(key).size === 256);
  fs.writeFileSync('tables.json', JSON.stringify({ target, key, sbox }));
  fs.writeFileSync('mem_1024.bin', Buffer.from(mem.slice(1024, 1568)));
  console.log('tables.json / mem_1024.bin (544B, __data_end=1568) ๊ธฐ๋ก ์™„๋ฃŒ');
});

๋– ๋‚ธ 544๋ฐ”์ดํŠธ๋ฅผ xxd๋กœ ํ™•์ธํ•˜๋ฉด ๊ฒฝ๊ณ„๊ฐ€ ๋ˆˆ์œผ๋กœ ๋ณด์ธ๋‹ค.

xxd -l 32 mem_1024.bin && echo "--- key  (์˜คํ”„์…‹ +32) ์ฒซ 32B ---" && xxd -s 32 -l 32 mem_1024.bin && echo "--- sbox (์˜คํ”„์…‹ +288) ์ฒซ 32B ---" && xxd -s 288 -l 32 mem_1024.bin

xxd ๋กœ ๋ณธ ๋ฉ”๋ชจ๋ฆฌ 1024๋ฒˆ์ง€ โ€” ์•ž 25๋ฐ”์ดํŠธ๊ฐ€ ๋น„๊ต ๋Œ€์ƒ target ์ด๊ณ  ์ด์–ด์ง€๋Š” 7๋ฐ”์ดํŠธ๊ฐ€ 0 ํŒจ๋”ฉ, ์˜คํ”„์…‹ +32 ๋ถ€ํ„ฐ key ๊ฐ€, +288 ๋ถ€ํ„ฐ sbox ๊ฐ€ ๋ถ™์–ด ์žˆ๋‹ค
xxd ๋กœ ๋ณธ ๋ฉ”๋ชจ๋ฆฌ 1024๋ฒˆ์ง€ โ€” ์•ž 25๋ฐ”์ดํŠธ๊ฐ€ ๋น„๊ต ๋Œ€์ƒ target ์ด๊ณ  ์ด์–ด์ง€๋Š” 7๋ฐ”์ดํŠธ๊ฐ€ 0 ํŒจ๋”ฉ, ์˜คํ”„์…‹ +32 ๋ถ€ํ„ฐ key ๊ฐ€, +288 ๋ถ€ํ„ฐ sbox ๊ฐ€ ๋ถ™์–ด ์žˆ๋‹ค

6e00 0000 0000 0000 ์ž๋ฆฌ๊ฐ€ ๋ณด์ธ๋‹ค. target์˜ ๋งˆ์ง€๋ง‰ ๋ฐ”์ดํŠธ 0x6e ๋’ค๋กœ 0์ด ์ผ๊ณฑ ๊ฐœ. ์˜ˆ์ƒํ•œ ํŒจ๋”ฉ์ด๋‹ค.

node dump_tables.js

node dump_tables.js ์ถœ๋ ฅ โ€” target 25๋ฐ”์ดํŠธ ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ์ฐํžˆ๊ณ , key ์™€ sbox ๋‘˜ ๋‹ค ์ค‘๋ณต ์—†๋Š” 256๊ฐœ ์ˆœ์—ด์ด๋ผ๋Š” ํŒ์ •์ด true ๋กœ ๋‚˜์˜จ๋‹ค
node dump_tables.js ์ถœ๋ ฅ โ€” target 25๋ฐ”์ดํŠธ ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ์ฐํžˆ๊ณ , key ์™€ sbox ๋‘˜ ๋‹ค ์ค‘๋ณต ์—†๋Š” 256๊ฐœ ์ˆœ์—ด์ด๋ผ๋Š” ํŒ์ •์ด true ๋กœ ๋‚˜์˜จ๋‹ค

key๋„ sbox๋„ 256๊ฐœ ๊ฐ’์ด ์ „๋ถ€ ๋‹ค๋ฅด๋‹ค. ๋‘˜ ๋‹ค ์ˆœ์—ด์ด๋ผ๋Š” ๋œป์ด๊ณ , ๊ทธ๋Ÿฌ๋ฉด ์œ„์น˜๋ณ„ ์น˜ํ™˜ ์‚ฌ์Šฌ ์ „์ฒด๊ฐ€ 0..255 ์œ„์˜ ์ „๋‹จ์‚ฌ๋‹ค. ์—ญ์ƒ์ด ์กด์žฌํ•˜๊ณ , ์ •ํ™•ํžˆ ํ•˜๋‚˜๋‹ค.

์ด ์ง€์ ์—์„œ ํ’€์ด ๋ฐฉํ–ฅ์ด ์ •ํ•ด์ง„๋‹ค. ์‚ฌ์Šฌ์„ ๋Œ€์ˆ˜์ ์œผ๋กœ ๋’ค์ง‘์„ ํ•„์š”๋„ ์—†๋‹ค. ์œ„์น˜๋งˆ๋‹ค ํ›„๋ณด 256๊ฐœ๋ฅผ ์ •๋ฐฉํ–ฅ์œผ๋กœ ํƒœ์›Œ ๋ณด๊ณ  ๋งž๋Š” ๊ฑธ ๊ณ ๋ฅด๋ฉด ๋œ๋‹ค. 25 ร— 256 = 6,400๋ฒˆ. ๋ˆˆ ๊นœ์งํ•  ์ƒˆ๋‹ค.



๐Ÿ› ๋น ์ง€๊ธฐ ์‰ฌ์šด ๋‘ ๊ณณ

โ–ถ๐Ÿ•ณ๏ธ ์‚ฝ์งˆ โ€” ํšŒ์ „ ๋ฐฉํ–ฅ๊ณผ ๋’ค์ง‘๊ธฐ๋ฅผ ๋น ๋œจ๋ฆฌ๋ฉด ์กฐ์šฉํžˆ ํ‹€๋ฆฐ ๋‹ต์ด ๋‚˜์˜จ๋‹ค

์ด ๋ฌธ์ œ์˜ ๊ณ ์•ฝํ•œ ์ ์€ ํ‹€๋ ค๋„ ํ‹ฐ๊ฐ€ ์•ˆ ๋‚œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

ํšŒ์ „ ๋ฐฉํ–ฅ์„ ํ™€์ง์œผ๋กœ ์•ˆ ๊ฐ€๋ฅด๊ณ  ํ•œ ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ ๊ณ ์ •ํ•ด๋„, ๋งˆ์ง€๋ง‰ ๋’ค์ง‘๊ธฐ๋ฅผ ๋ณด์ • ์•ˆ ํ•ด๋„, ์‚ฌ์Šฌ์€ ์—ฌ์ „ํžˆ ์ „๋‹จ์‚ฌ๋‹ค. "์—ญ์ƒ์ด ์œ ์ผํ•˜๋‹ค"๋Š” assert๋„ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผํ•œ๋‹ค. ๊ทธ๋ƒฅ ๋‹ค๋ฅธ 25๋ฐ”์ดํŠธ๊ฐ€ ๋‚˜์˜ฌ ๋ฟ์ด๋‹ค.

์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€ ๊ฐ๊ฐ ๋นผ๊ณ  ๋Œ๋ ค์„œ ํ™•์ธํ•ด ๋’€๋‹ค.

#!/usr/bin/env python3
"""์—ญ์‚ฐ์—์„œ ๋น ๋œจ๋ฆฌ๊ธฐ ์‰ฌ์šด ๋‘ ๊ฐ€์ง€๋ฅผ ์‹ค์ œ๋กœ ๋น ๋œจ๋ ค ๋ณด๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋น„๊ตํ•œ๋‹ค.
 
(1) ํšŒ์ „ ๋ฐฉํ–ฅ์„ i ์˜ ํ™€์ง์œผ๋กœ ๊ฐ€๋ฅด์ง€ ์•Š๊ณ  ํ•œ ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ ๊ณ ์ •
(2) ๋งˆ์ง€๋ง‰ buf ๋’ค์ง‘๊ธฐ(swap i <-> 24-i)๋ฅผ ๋ณด์ •ํ•˜์ง€ ์•Š์Œ
๋‘˜ ๋‹ค ์‚ฌ์Šฌ์€ ์—ฌ์ „ํžˆ ์ „๋‹จ์‚ฌ๋ผ "์—ญ์ƒ ์œ ์ผ" ๊ฒ€์ฆ์„ ํ†ต๊ณผํ•œ๋‹ค โ€” ๊ทธ๋ž˜์„œ ์กฐ์šฉํžˆ ํ‹€๋ฆฐ ๋‹ต์ด ๋‚˜์˜จ๋‹ค.
"""
import json, pathlib
 
T = json.loads(pathlib.Path("tables.json").read_text())
target, key, sbox = T["target"], T["key"], T["sbox"]
 
def ror8(v, n): return ((v >> n) | (v << (8 - n))) & 0xFF if n else v
def rol8(v, n): return ((v << n) | (v >> (8 - n))) & 0xFF if n else v
 
def chain(j, v, parity=True):
    for i in range(j, 256, 25):
        v ^= key[i]
        a = i & 7
        if parity:
            v = ror8(v, a) if i % 2 == 0 else rol8(v, a)
        else:
            v = ror8(v, a)          # (1) ํ•ญ์ƒ ์˜ค๋ฅธ์ชฝ ํšŒ์ „
        v = sbox[v]
    return v
 
def invert(parity=True, reverse=True):
    out = bytearray(25)
    for j in range(25):
        want = target[24 - j] if reverse else target[j]
        c = [x for x in range(256) if chain(j, x, parity) == want]
        out[j] = c[0] if len(c) == 1 else 0x3F
    return bytes(out)
 
def show(label, b):
    printable = "".join(chr(x) if 0x20 <= x < 0x7F else "." for x in b)
    print(f"{label:<34} {printable}")
 
show("(1) ํšŒ์ „ ๋ฐฉํ–ฅ ๊ณ ์ •", invert(parity=False, reverse=True))
show("(2) ๋’ค์ง‘๊ธฐ ๋ณด์ • ์—†์Œ", invert(parity=True, reverse=False))
show("(1)+(2) ๋‘˜ ๋‹ค ๋น ๋œจ๋ฆผ", invert(parity=False, reverse=False))
show("๋‘˜ ๋‹ค ๋ฐ˜์˜ํ•œ ์ •๋‹ต", invert(parity=True, reverse=True))
python3 pitfall.py

pitfall.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ํšŒ์ „ ๋ฐฉํ–ฅ์„ ๊ณ ์ •ํ•˜๊ฑฐ๋‚˜ ๋’ค์ง‘๊ธฐ๋ฅผ ๋นผ๋ฉด ์ถœ๋ ฅ์ด ์ „๋ถ€ ๊นจ์ง„ ๋ฐ”์ดํŠธ๊ฐ€ ๋˜๊ณ , ๋‘˜ ๋‹ค ๋ฐ˜์˜ํ–ˆ์„ ๋•Œ๋งŒ ์ฝํžˆ๋Š” passcode ๊ฐ€ ๋‚˜์˜จ๋‹ค
pitfall.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ํšŒ์ „ ๋ฐฉํ–ฅ์„ ๊ณ ์ •ํ•˜๊ฑฐ๋‚˜ ๋’ค์ง‘๊ธฐ๋ฅผ ๋นผ๋ฉด ์ถœ๋ ฅ์ด ์ „๋ถ€ ๊นจ์ง„ ๋ฐ”์ดํŠธ๊ฐ€ ๋˜๊ณ , ๋‘˜ ๋‹ค ๋ฐ˜์˜ํ–ˆ์„ ๋•Œ๋งŒ ์ฝํžˆ๋Š” passcode ๊ฐ€ ๋‚˜์˜จ๋‹ค

์…‹ ๋‹ค 25๋ฐ”์ดํŠธ๋ฅผ ๋ฉ€์ฉกํžˆ ๋ฑ‰๋Š”๋‹ค. ๊ทธ์ € ์ฝ์„ ์ˆ˜ ์—†๋Š” ๋ฐ”์ดํŠธ์ผ ๋ฟ์ด๋‹ค. ๋งŒ์•ฝ ์ •๋‹ต์ด ๋žœ๋ค ๋ฐ”์ดํŠธ์—ด์ด์—ˆ๋‹ค๋ฉด ๋„ท ์ค‘ ์–ด๋А ๊ฒŒ ๋งž๋Š”์ง€ ํ™”๋ฉด๋งŒ ๋ณด๊ณ ๋Š” ๋ชป ๊ณจ๋ž์„ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ solve.py์—๋Š” ์ •๋ฐฉํ–ฅ ์žฌํ˜„ assert๋ฅผ ๋„ฃ์—ˆ๋‹ค. ๋ฝ‘์€ ๋‹ต์„ ๋‹ค์‹œ f()๊ฐ€ ํ•˜๋Š” ์ˆœ์„œ๋Œ€๋กœ ํƒœ์›Œ target๊ณผ ๊ธ€์ž ํ•˜๋‚˜๊นŒ์ง€ ๋งž๋Š”์ง€ ์Šค์Šค๋กœ ํ™•์ธํ•œ๋‹ค.



๐ŸŽฏ ์—ญ์‚ฐ โ€” ์œ„์น˜๋งˆ๋‹ค 256๊ฐœ๋งŒ ๋Œ๋ ค ๋ณธ๋‹ค

์—ญ์‚ฐ ์ฝ”๋“œ๋Š” ์งง๋‹ค. f()๊ฐ€ ์œ„์น˜ j์— ๊ฐ€ํ•˜๋Š” ์—ฐ์‚ฐ์„ ๊ทธ๋Œ€๋กœ ํ•จ์ˆ˜ ํ•˜๋‚˜๋กœ ์˜ฎ๊ธฐ๊ณ , ๊ทธ๊ฑธ 256๋ฒˆ ํ˜ธ์ถœํ•œ๋‹ค.

๋’ค์ง‘๊ธฐ๋Š” ๋งˆ์ง€๋ง‰์— ํ•œ ๋ฒˆ๋ฟ์ด๋‹ˆ ์ธ๋ฑ์Šค ํ•˜๋‚˜๋กœ ํก์ˆ˜๋œ๋‹ค. ์œ„์น˜ j์˜ ๋ณ€ํ™˜ ๊ฒฐ๊ณผ๋Š” ๋’ค์ง‘ํžŒ ๋’ค 24-j๋ฒˆ ์ž๋ฆฌ๋กœ ๊ฐ€๋ฏ€๋กœ, chain(j, x)๊ฐ€ target[24-j]์™€ ๊ฐ™์•„์•ผ ํ•œ๋‹ค.

#!/usr/bin/env python3
"""passcode (DreamHack, Gold 1 / reversing) โ€” wasm export f() ์—ญ์‚ฐ
 
f(ptr,len) ์ด ํ•˜๋Š” ์ผ (passcode.wat ์—์„œ ์ฝ์–ด๋‚ธ ๊ทธ๋Œ€๋กœ):
  len != 25 ์ด๋ฉด ์ฆ‰์‹œ 255 ๋ฐ˜ํ™˜.
  for i in range(256):
      j = (i & 0xff) % 25                 # ๋ฐ”์ดํŠธ ์œ„์น˜. i ๊ฐ€ 256 ๋ฏธ๋งŒ์ด๋ผ i % 25 ์™€ ๊ฐ™๋‹ค
      v = buf[j] ^ key[i]                 # key = ๋ฉ”๋ชจ๋ฆฌ 1056.. 256๋ฐ”์ดํŠธ
      a, b = i & 7, (-i) & 7
      v = ror8(v, a) if i % 2 == 0 else rol8(v, a)
      buf[j] = sbox[v]                    # sbox = ๋ฉ”๋ชจ๋ฆฌ 1312.. 256๋ฐ”์ดํŠธ
  buf ๋ฅผ ๋’ค์ง‘๊ณ  memcmp(buf, target@1024, 25).
 
ํ•ต์‹ฌ์€ j = i % 25 ๋ผ์„œ **๋ฐ”์ดํŠธ๋ผ๋ฆฌ ์ ˆ๋Œ€ ์„ž์ด์ง€ ์•Š๋Š”๋‹ค**๋Š” ๊ฒƒ. 25๊ฐœ ์œ„์น˜๊ฐ€
๊ฐ์ž ๋…๋ฆฝ์ ์ธ ์น˜ํ™˜ ์‚ฌ์Šฌ์„ ํƒ„๋‹ค. sbox ๋„ key ๋„ ์ˆœ์—ด์ด๋ผ ์‚ฌ์Šฌ ์ „์ฒด๊ฐ€ ์ „๋‹จ์‚ฌ์ด๋ฏ€๋กœ
์œ„์น˜๋งˆ๋‹ค 256๊ฐœ ํ›„๋ณด๋ฅผ ๊ทธ๋ƒฅ ๋‹ค ๋Œ๋ ค ๋ณด๋ฉด ์—ญ์ƒ์ด ํ•˜๋‚˜๋กœ ๋–จ์–ด์ง„๋‹ค.
๋’ค์ง‘๊ธฐ๊นŒ์ง€ ๊ฐ์•ˆํ•˜๋ฉด ์œ„์น˜ j ์˜ ๊ฒฐ๊ณผ๊ฐ€ target[24-j] ์—ฌ์•ผ ํ•œ๋‹ค.
"""
import json, pathlib
 
T = json.loads(pathlib.Path("tables.json").read_text())
target, key, sbox = T["target"], T["key"], T["sbox"]
 
def ror8(v, n): return ((v >> n) | (v << (8 - n))) & 0xFF if n else v
def rol8(v, n): return ((v << n) | (v >> (8 - n))) & 0xFF if n else v
 
def chain(j, v):
    """์œ„์น˜ j ์˜ ๋ฐ”์ดํŠธ v ์— f() ๊ฐ€ ์ ์šฉํ•˜๋Š” ์—ฐ์‚ฐ์„ ์ˆœ์„œ๋Œ€๋กœ ๋‹ค ํƒœ์šด๋‹ค."""
    for i in range(j, 256, 25):
        v ^= key[i]
        a = i & 7
        v = ror8(v, a) if i % 2 == 0 else rol8(v, a)
        v = sbox[v]
    return v
 
passcode = bytearray(25)
for j in range(25):
    want = target[24 - j]                       # ๋’ค์ง‘๊ธฐ ๋ณด์ •
    cands = [c for c in range(256) if chain(j, c) == want]
    assert len(cands) == 1, f"j={j} ํ›„๋ณด {len(cands)}๊ฐœ โ€” ์‚ฌ์Šฌ์ด ์ „๋‹จ์‚ฌ๊ฐ€ ์•„๋‹ˆ๋‹ค"
    passcode[j] = cands[0]
 
# ์ž์ฒด ๊ฒ€์ฆ: ์ •๋ฐฉํ–ฅ์œผ๋กœ ๋‹ค์‹œ ๋Œ๋ ค target ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค
fwd = [chain(j, passcode[j]) for j in range(25)][::-1]
assert fwd == target, "์ •๋ฐฉํ–ฅ ์žฌํ˜„ ์‹คํŒจ"
 
print("passcode =", passcode.decode())
print("FLAG     = DH{%s}" % passcode.decode())
python3 solve.py

python3 solve.py ์‹คํ–‰ โ€” ์œ„์น˜๋ณ„ 256ํ›„๋ณด ์ „์ˆ˜์™€ ์ •๋ฐฉํ–ฅ ์žฌํ˜„ assert ๋ฅผ ๋ชจ๋‘ ํ†ต๊ณผํ•˜๊ณ  25์ž passcode ์™€ DH ํ˜•์‹ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค
python3 solve.py ์‹คํ–‰ โ€” ์œ„์น˜๋ณ„ 256ํ›„๋ณด ์ „์ˆ˜์™€ ์ •๋ฐฉํ–ฅ ์žฌํ˜„ assert ๋ฅผ ๋ชจ๋‘ ํ†ต๊ณผํ•˜๊ณ  25์ž passcode ์™€ DH ํ˜•์‹ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค

W3B@sSemb1y_R3ver5in9_^-^. 25๊ธ€์ž, ๋ฆฌํŠธ ํ‘œ๊ธฐ๋กœ "WebAssembly Reversing". ๋งˆ์ง€๋ง‰ ^-^๊นŒ์ง€ ํฌํ•จํ•ด์„œ ๊ธธ์ด๊ฐ€ ๋”ฑ 25๋‹ค.



๐Ÿš€ ๊ฒ€์ฆ โ€” ์ง„์งœ wasm๊ณผ ์ง„์งœ ๋ธŒ๋ผ์šฐ์ €

์—ญ์‚ฐ์ด ๋งž๋Š”์ง€๋Š” ๋ฐฐํฌ๋ณธ wasm์— ์ง์ ‘ ๋ฌผ์–ด๋ณด๋Š” ๊ฒŒ ์ œ์ผ ํ™•์‹คํ•˜๋‹ค. main.js์˜ ํผ ํ•ธ๋“ค๋Ÿฌ์™€ ๋˜‘๊ฐ™์€ ์ˆœ์„œ๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ์จ ๋„ฃ๊ณ  f()๋ฅผ ๋ถ€๋ฅธ๋‹ค.

// solve.py ๊ฐ€ ๋ฝ‘์€ passcode ๋ฅผ main.js ์™€ ๋˜‘๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ง„์งœ wasm ์— ๋จน์ธ๋‹ค.
// f() ๊ฐ€ 0 ์„ ๋Œ๋ ค์ฃผ๋ฉด ๋ธŒ๋ผ์šฐ์ €์—์„œ Correct! ๊ฐ€ ๋œจ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์กฐ๊ฑด์ด๋‹ค.
const fs = require('fs');
const code = new Uint8Array(fs.readFileSync('passcode.wasm'));
const passcode = process.argv[2] || 'W3B@sSemb1y_R3ver5in9_^-^';
 
WebAssembly.instantiate(code).then(({ instance }) => {
  const array = new Uint8Array(instance.exports.memory.buffer, 0, passcode.length);
  array.set(passcode.split('').map(x => x.charCodeAt()));
  const result = instance.exports.f(array.byteOffset, array.length);
  console.log(`passcode = ${passcode}  (len=${passcode.length})`);
  console.log(`f() -> ${result}   ${result === 0 ? '=> Correct!  FLAG = DH{' + passcode + '}' : '=> Wrong!'}`);
  // ๋Œ€์กฐ๊ตฐ: ํ•œ ๊ธ€์ž๋งŒ ๋ฐ”๊พธ๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€
  const bad = 'X' + passcode.slice(1);
  const a2 = new Uint8Array(instance.exports.memory.buffer, 0, bad.length);
  a2.set(bad.split('').map(x => x.charCodeAt()));
  console.log(`๋Œ€์กฐ๊ตฐ "${bad}" -> f() = ${instance.exports.f(a2.byteOffset, a2.length)} (0 ์ด ์•„๋‹ˆ๋ฉด Wrong)`);
});
node verify.js

node verify.js ์‹คํ–‰ โ€” ๋ฐฐํฌ๋ณธ wasm ์— ๊ทธ๋Œ€๋กœ ๋จน์—ฌ f() ๊ฐ€ 0 ์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ฒซ ๊ธ€์ž๋งŒ X ๋กœ ๋ฐ”๊พผ ๋Œ€์กฐ๊ตฐ์€ 202 ๋ฅผ ๋Œ๋ ค์ค€๋‹ค
node verify.js ์‹คํ–‰ โ€” ๋ฐฐํฌ๋ณธ wasm ์— ๊ทธ๋Œ€๋กœ ๋จน์—ฌ f() ๊ฐ€ 0 ์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ฒซ ๊ธ€์ž๋งŒ X ๋กœ ๋ฐ”๊พผ ๋Œ€์กฐ๊ตฐ์€ 202 ๋ฅผ ๋Œ๋ ค์ค€๋‹ค

0์ด๋‹ค. ๋Œ€์กฐ๊ตฐ์œผ๋กœ ์ฒซ ๊ธ€์ž๋งŒ X๋กœ ๋ฐ”๊ฟจ๋”๋‹ˆ 202๊ฐ€ ๋‚˜์˜จ๋‹ค. ํŒ์ •์ด ์‹ค์ œ๋กœ ์ž…๋ ฅ์— ๋ฐ˜์‘ํ•œ๋‹ค๋Š” ํ™•์ธ์ด๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€ ์™”์œผ๋ฉด ๋ธŒ๋ผ์šฐ์ €๋กœ๋„ ๋ด์•ผ ํ•œ๋‹ค. ์„œ๋ฒ„๊ฐ€ ์—†๋Š” ๋ฌธ์ œ๋ผ extracted/ ํด๋”๋ฅผ ๊ทธ๋Œ€๋กœ ๋กœ์ปฌ์—์„œ ์„œ๋น™ํ–ˆ๋‹ค.

cd extracted && python3 -m http.server 8412 --bind 127.0.0.1

๋กœ์ปฌ http.server ๋กœ ๋„์šด ๋ฌธ์ œ ํŽ˜์ด์ง€ โ€” ์ฃผ์†Œ์ฐฝ์€ 127.0.0.1:8412 ์ด๊ณ  ๋ณด๋ผ์ƒ‰ ๋ฐฐ๊ฒฝ์— ์—ด์‡  ์•„์ด์ฝ˜์ด ๋ถ™์€ ์ž…๋ ฅ์นธ ํ•˜๋‚˜๋ฟ์ธ ์›๋ž˜ ํ™”๋ฉด์ด๋‹ค
๋กœ์ปฌ http.server ๋กœ ๋„์šด ๋ฌธ์ œ ํŽ˜์ด์ง€ โ€” ์ฃผ์†Œ์ฐฝ์€ 127.0.0.1:8412 ์ด๊ณ  ๋ณด๋ผ์ƒ‰ ๋ฐฐ๊ฒฝ์— ์—ด์‡  ์•„์ด์ฝ˜์ด ๋ถ™์€ ์ž…๋ ฅ์นธ ํ•˜๋‚˜๋ฟ์ธ ์›๋ž˜ ํ™”๋ฉด์ด๋‹ค

์ž…๋ ฅ์นธ ํ•˜๋‚˜๊ฐ€ ์ „๋ถ€๋‹ค. ๋จผ์ € ํ‹€๋ฆฐ ๊ฐ’์„ ๋„ฃ์–ด ๋ณธ๋‹ค.

ํ‹€๋ฆฐ passcode ๋ฅผ ๋„ฃ๊ณ  ์—”ํ„ฐ๋ฅผ ์ณค์„ ๋•Œ โ€” SweetAlert2 ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ Wrong! You have entered a wrong passcode ๋ฌธ๊ตฌ๋กœ ๋œฌ๋‹ค
ํ‹€๋ฆฐ passcode ๋ฅผ ๋„ฃ๊ณ  ์—”ํ„ฐ๋ฅผ ์ณค์„ ๋•Œ โ€” SweetAlert2 ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ Wrong! You have entered a wrong passcode ๋ฌธ๊ตฌ๋กœ ๋œฌ๋‹ค

๊ทธ๋ฆฌ๊ณ  ์—ญ์‚ฐํ•œ ๊ฐ’.

์—ญ์‚ฐํ•œ passcode ๋ฅผ ๋„ฃ์—ˆ์„ ๋•Œ โ€” ๊ฐ™์€ ํŽ˜์ด์ง€๊ฐ€ Correct! You have entered a correct passcode ๋กœ ๋ฐ”๋€๋‹ค
์—ญ์‚ฐํ•œ passcode ๋ฅผ ๋„ฃ์—ˆ์„ ๋•Œ โ€” ๊ฐ™์€ ํŽ˜์ด์ง€๊ฐ€ Correct! You have entered a correct passcode ๋กœ ๋ฐ”๋€๋‹ค

๋งˆ์ง€๋ง‰์œผ๋กœ DevTools ์ฝ˜์†”์—์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ง์ ‘ ๋งŒ์ ธ ๋ดค๋‹ค. ํŽ˜์ด์ง€ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ instance์™€ passcode๋ฅผ ์ „์—ญ์œผ๋กœ ๋‘๊ธฐ ๋•Œ๋ฌธ์— ์ฝ˜์†”์—์„œ ๊ทธ๋Œ€๋กœ ์žกํžŒ๋‹ค.

Object.keys(instance.exports).join()
passcode
buf=new Uint8Array(instance.exports.memory.buffer,0,25),buf.set(Array.from(passcode).map(c=>c.charCodeAt())),instance.exports.f(0,25)

Chrome DevTools ์ฝ˜์†”์—์„œ ์ง์ ‘ ํ™•์ธ โ€” instance.exports ๋Š” memory ์™€ __heap_base, __data_end, f ๋„ท์ด๊ณ  f(0,25) ํ˜ธ์ถœ์ด 0 ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค
Chrome DevTools ์ฝ˜์†”์—์„œ ์ง์ ‘ ํ™•์ธ โ€” instance.exports ๋Š” memory ์™€ __heap_base, __data_end, f ๋„ท์ด๊ณ  f(0,25) ํ˜ธ์ถœ์ด 0 ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค

๋‚ด๋ณด๋‚ด๊ธฐ ๋ชฉ๋ก์ด wasm-objdump๋กœ ๋ณธ ๊ฒƒ๊ณผ ๊ฐ™๊ณ , f(0, 25)๊ฐ€ ์ฝ˜์†”์—์„œ๋„ 0์„ ์ค€๋‹ค. ์ •์  ๋ถ„์„๊ณผ ๋Ÿฐํƒ€์ž„์ด ๊ฐ™์€ ์–˜๊ธฐ๋ฅผ ํ•˜๊ณ  ์žˆ๋‹ค.

ํ”Œ๋ž˜๊ทธ๋Š” passcode๋ฅผ ํ˜•์‹์— ๋„ฃ์€ ๊ทธ๋Œ€๋กœ๋‹ค.

DH{W3B@sSemb1y_R3ver5in9_^-^}

ํ•œ ๋ฐฉ ์žฌํ˜„

๋ฌธ์ œ ํด๋”์— reproduce.sh๋ฅผ ๋‚จ๊ฒจ ๋’€๋‹ค. ์ถ”์ถœ๋ถ€ํ„ฐ ๊ฒ€์ฆ๊นŒ์ง€ ํ•œ ๋ฒˆ์— ๋ˆ๋‹ค. ํ•„์š”ํ•œ ๊ฑด python3๊ณผ node๋ฟ์ด๊ณ , wabt๋Š” ๋ถ„์„์šฉ์ด๋ผ ์žฌํ˜„์—๋Š” ์“ฐ์ง€ ์•Š๋Š”๋‹ค.

#!/usr/bin/env bash
# passcode (DreamHack Gold 1 / reversing) โ€” ํ•œ ๋ฐฉ ์žฌํ˜„.
#   extracted/main.js ์—์„œ wasm ์ถ”์ถœ โ†’ ์ƒ์ˆ˜ ํ…Œ์ด๋ธ” ๋คํ”„ โ†’ ์—ญ์‚ฐ โ†’ ์ง„์งœ wasm ์œผ๋กœ ๊ฒ€์ฆ๊นŒ์ง€.
# ํ•„์š”ํ•œ ๊ฒƒ: python3, node (wabt ๋Š” ๋ถ„์„์šฉ์ด๋ผ ์—ฌ๊ธฐ์„  ์•ˆ ์“ด๋‹ค)
set -eu
cd "$(dirname "$(readlink -f "$0")")"
 
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ œ.json'))['flag'])" 2>/dev/null || echo '')
 
command -v node >/dev/null || { echo "โŒ node ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค"; exit 1; }
 
python3 extract_wasm.py                 # extracted/main.js โ†’ passcode.wasm
node    dump_tables.js                  # ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ โ†’ tables.json / mem_1024.bin
OUT=$(timeout 120 python3 solve.py 2>&1) || true
echo "$OUT"
 
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
PASS=$(printf '%s' "$FLAG" | sed -E 's/^DH\{(.*)\}$/\1/')
 
echo
echo "--- ์ง„์งœ wasm ์œผ๋กœ ์žฌ๊ฒ€์ฆ (f() ๊ฐ€ 0 ์ด์–ด์•ผ ์ •๋‹ต) ---"
node verify.js "$PASS"
 
if [ -n "$FLAG" ] && node verify.js "$PASS" | grep -q 'f() -> 0'; then
  if [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; then
    echo; echo "โœ… PASS  $FLAG"; exit 0
  fi
fi
echo; echo "โŒ FAIL (์–ป์€ ๊ฐ’: '${FLAG:-์—†์Œ}' / ๊ธฐ๋Œ€: '${EXPECT:-๋Œ€์กฐ๋ณธ ์—†์Œ}')"; exit 1

๋กœ์ปฌ ํ™˜๊ฒฝ์€ Ubuntu 25.10(์ปค๋„ 6.17.0-41) ยท Node v22.12.0 ยท Python 3.13.7 ยท wabt๋Š” npm i wabt๋กœ ๋ฐ›์€ ๊ฒƒ(ํŒจํ‚ค์ง€ ๋ฒ„์ „์€ package-lock.json์— ๊ณ ์ •)์„ ์ผ๋‹ค. ํด๋”๋ฅผ ํ†ต์งธ๋กœ ๋‹ค๋ฅธ ๊ฒฝ๋กœ์— ๋ณต์‚ฌํ•ด ์‚ฐ์ถœ๋ฌผ์„ ์ง€์šฐ๊ณ  ํ•œ ๋ฒˆ ๋” ๋Œ๋ ค PASS๊ฐ€ ๋‚˜์˜ค๋Š” ๊ฒƒ๊นŒ์ง€ ํ™•์ธํ–ˆ๋‹ค.



๐Ÿ“ ๊ฒฐ๋ก 

์„ž์ง€ ์•Š๋Š” ์•”ํ˜ธ๋Š” ์•”ํ˜ธ๊ฐ€ ์•„๋‹ˆ๋‹ค

์ด ๋ฌธ์ œ์˜ ์ „๋ถ€๋Š” (c & 255) % 25 ํ•œ ์ค„์ด๋‹ค. ๋ฃจํ”„๋ฅผ 256๋ฒˆ ๋Œ๋ฆฌ๊ณ , XOR๊ณผ ํšŒ์ „๊ณผ S-box๋ฅผ ๊ฒน์ณ ๋†“๊ณ , ๋งˆ์ง€๋ง‰์— ๋’ค์ง‘๊ธฐ๊นŒ์ง€ ์–น์—ˆ์ง€๋งŒ, ๋ฐ”์ดํŠธ ์‚ฌ์ด์— ํ™•์‚ฐ(diffusion)์ด ์—†๋‹ค. ๊ทธ๋ž˜์„œ 25๋ฐ”์ดํŠธ ํƒ์ƒ‰ ๊ณต๊ฐ„์ด 25๊ฐœ์˜ 256์งœ๋ฆฌ ํƒ์ƒ‰์œผ๋กœ ๋ฌด๋„ˆ์ง„๋‹ค.

๋ธ”๋ก ์•”ํ˜ธ๊ฐ€ ์™œ ๋งค ๋ผ์šด๋“œ ์ž๋ฆฌ๋ฅผ ์„ž๋Š”์ง€๋ฅผ ๋ฐ˜๋Œ€ํŽธ์—์„œ ๋ณด์—ฌ ์ฃผ๋Š” ์…ˆ์ด๋‹ค. ์น˜ํ™˜๋งŒ ์Œ“์•„ ๋ด์•ผ ์œ„์น˜๋ณ„ ์ „๋‹จ์‚ฌ๊ฐ€ ํ•˜๋‚˜ ๋” ๊ธธ์–ด์งˆ ๋ฟ์ด๋‹ค.

์ •์  ๋ถ„์„์€ ๋„๊ตฌ๋ฅผ ๋ฐ”๊ฟ” ๊ฐ€๋ฉฐ ๋‘ ๋ฒˆ ๋ณธ๋‹ค

wasm-decompile์˜ C ๋น„์Šทํ•œ ์ถœ๋ ฅ์ด ์—†์—ˆ๋‹ค๋ฉด WAT 76์ค„์„ ์Šคํƒ ๋จธ์‹  ๊ทธ๋Œ€๋กœ ๋”ฐ๋ผ๊ฐ€์•ผ ํ–ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ wasm-decompile๋งŒ ๋ดค๋‹ค๋ฉด i32.rem_u๊ฐ€ ์–ด๋А ๊ฐ’์— ๊ฑธ๋ฆฌ๋Š”์ง€ ํ—ท๊ฐˆ๋ ธ์„ ๊ฒƒ์ด๋‹ค. ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ๋‘ ํ‘œํ˜„์œผ๋กœ ๋ณด๋Š” ๊ฒŒ ๋น ๋ฅด๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ƒ์ˆ˜๋Š” ํŒŒ์‹ฑํ•˜์ง€ ๋ง๊ณ  ์‹คํ–‰ํ•ด์„œ ๊บผ๋‚ด๋Š” ๊ฒŒ ๋‚ซ๋‹ค. WebAssembly.instantiate ํ•œ ์ค„์ด๋ฉด ๋ฐ์ดํ„ฐ ์„น์…˜์ด ์•Œ์•„์„œ ๋ฉ”๋ชจ๋ฆฌ์— ๊น”๋ฆฐ๋‹ค. LEB128์„ ์†์œผ๋กœ ์„ธ๋‹ค ํ•œ ๋ฐ”์ดํŠธ ํ‹€๋ฆฌ๋ฉด ๋‹ต ์ „์ฒด๊ฐ€ ์–ด๊ธ‹๋‚œ๋‹ค.

ํ‹€๋ ค๋„ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค๋Š” ์—ญ์‚ฐ์€ ์Šค์Šค๋กœ ๊ฒ€์ฆํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค

ํšŒ์ „ ๋ฐฉํ–ฅ์ด๋‚˜ ๋’ค์ง‘๊ธฐ๋ฅผ ๋น ๋œจ๋ ค๋„ 25๋ฐ”์ดํŠธ๊ฐ€ ๋ฉ€์ฉกํžˆ ๋‚˜์˜จ๋‹ค. ์ •๋‹ต์ด ์‚ฌ๋žŒ์ด ์ฝ๋Š” ๋ฌธ์ž์—ด์ด๋ผ ๋ˆˆ์œผ๋กœ ๊ฑธ๋Ÿฌ์กŒ์„ ๋ฟ์ด๋‹ค. solve.py๊ฐ€ ๋ฝ‘์€ ๋‹ต์„ ์ •๋ฐฉํ–ฅ์œผ๋กœ ๋‹ค์‹œ ํƒœ์›Œ target๊ณผ ๋Œ€์กฐํ•˜๊ณ , ๋งˆ์ง€๋ง‰์—๋Š” ๋ฐฐํฌ๋ณธ wasm์— ์ง์ ‘ ๋จน์—ฌ 0์„ ๋ฐ›๋Š” ๊ฒƒ๊นŒ์ง€ ํ•ด ๋‘๋ฉด ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ ์ฐฉ๊ฐ์ด ๋‚จ์ง€ ์•Š๋Š”๋‹ค.

ํด๋ผ์ด์–ธํŠธ์— ๊ฒ€์ฆ ๋กœ์ง์„ ๋‘๋ฉด ๊ทธ๊ฒŒ ๊ณง ์ •๋‹ต ์‚ฌ๋ณธ์ด๋‹ค

๋ฐฉ์–ด ๊ด€์ ์—์„œ๋Š” ๋ป”ํ•œ ๊ฒฐ๋ก ์ด ํ•˜๋‚˜ ๋‚จ๋Š”๋‹ค. ์ด ํŽ˜์ด์ง€๋Š” ์ •๋‹ต์„ ์„œ๋ฒ„์— ์•ˆ ๋ฌผ์–ด๋ณธ๋‹ค. ํŒ์ •์— ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฒƒ์ด ๋‹ค์šด๋กœ๋“œ๋œ 1,984๋ฐ”์ดํŠธ ์•ˆ์— ์žˆ๋‹ค. wasm์œผ๋กœ ๊ฐ์‹ธ๋ฉด JS๋ณด๋‹ค ์ฝ๊ธฐ ๋ฒˆ๊ฑฐ๋กœ์šธ ๋ฟ์ด์ง€, ๋„๊ตฌ๊ฐ€ ๊ฐ–์ถฐ์ง€๋ฉด ๋‚œ๋…ํ™” ์ถ•์—๋„ ๋ชป ๋“ ๋‹ค. import๊ฐ€ ํ•˜๋‚˜๋„ ์—†๋Š” ๋ชจ๋“ˆ์€ "์—ฌ๊ธฐ ์•ˆ์— ๋‹ต์ด ๋‹ค ์žˆ๋‹ค"๋Š” ํ‘œ์‹œ๋‚˜ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค.

์‹ค์ œ๋กœ ์ธ์ฆ์ด ํ•„์š”ํ•œ ๊ฐ’์ด๋ผ๋ฉด ์ž…๋ ฅ์„ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด ๊ฑฐ๊ธฐ์„œ๋งŒ ๋น„๊ตํ•ด์•ผ ํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ ๊ฒ€์‚ฌ๋Š” ์–ด๋””๊นŒ์ง€๋‚˜ ์˜คํƒ€ ๊ฑธ๋Ÿฌ ์ฃผ๋Š” ํŽธ์˜ ๊ธฐ๋Šฅ์œผ๋กœ ๋‘๋Š” ๊ฒŒ ๋งž๋‹ค.

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

Comments

๋Œ“๊ธ€

0๊ฐœ

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

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

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅ‡ Gold 4] WAT ์ •๋…์œผ๋กœ 68๋ฐ”์ดํŠธ ๋˜๋Œ๋ฆฌ๊ธฐ โ€” DreamHack Wasm rev for beginners ํ’€์ด
blog

[๐Ÿฅ‡ Gold 4] WAT ์ •๋…์œผ๋กœ 68๋ฐ”์ดํŠธ ๋˜๋Œ๋ฆฌ๊ธฐ โ€” DreamHack Wasm rev for beginners ํ’€์ด

๋ธŒ๋ผ์šฐ์ €์—์„œ ๋„๋Š” wasm-bindgen ์‚ฐ์ถœ๋ฌผ ํ•˜๋‚˜๊ฐ€ ์ „๋ถ€์ธ ๋ฆฌ๋ฒ„์‹ฑ ๋ฌธ์ œ. wabt ์œผ๋กœ WAT ์„ ๋ฝ‘์•„ check ํ•จ์ˆ˜๋ฅผ ์ฝ์œผ๋ฉด 68๋ฐ”์ดํŠธ XOR ํ‚ค์™€ x*13+37 ์•„ํ•€ ๋ณ€ํ™˜ ๋‘ ๋‹จ๊ณ„๊ฐ€ ์ „๋ถ€๋‹ค. 13 ์˜ mod 256 ์—ญ์› 197 ๋กœ ๋ธŒ๋ฃจํŠธํฌ์Šค ์—†์ด ํ”Œ๋ž˜๊ทธ๋ฅผ ํ•œ ๋ฒˆ์— ๋ณต์›ํ–ˆ๋‹ค.
#dreamhack#ctf#reversing+5
2026-08-20#dreamhack +6
[๐Ÿฅ‰ Bronze 4] AES S-box๋ฅผ ๊ฑฐ๊พธ๋กœ โ€” DreamHack rev-basic-6 ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 4] AES S-box๋ฅผ ๊ฑฐ๊พธ๋กœ โ€” DreamHack rev-basic-6 ํ’€์ด

์ž…๋ ฅ์„ ํ•œ ๊ธ€์ž์”ฉ 256๋ฐ”์ดํŠธ ์น˜ํ™˜ํ‘œ๋กœ ๋ฐ”๊ฟ” ๊ณ ์ •๋œ 18๋ฐ”์ดํŠธ์™€ ๋น„๊ตํ•˜๋Š” x86-64 PE. ๊ทธ ์น˜ํ™˜ํ‘œ๊ฐ€ AES S-box๋ผ๋Š” ๊ฑธ ์•Œ์•„์ฑ„๋ฉด ๋๋‚œ๋‹ค. S-box๋ฅผ ํ•œ ๋ฒˆ ๋’ค์ง‘์–ด ๊ธฐ๋Œ€๊ฐ’์„ ์—ญ์น˜ํ™˜ํ•˜๋ฉด ์ž…๋ ฅ์ด ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค. ๋””์Šค์–ด์…ˆ๋ธ”๋กœ ๊ฒ€์ฆ ๋กœ์ง์„ ์ฝ๊ณ , ํ‘œ๋ฅผ ์ถ”์ถœํ•ด ์—ญ์‚ฐํ•˜๊ณ , wine์œผ๋กœ Correct๋ฅผ ํ™•์ธํ•œ ๊ณผ์ •.
#dreamhack#ctf#reversing+5
2026-07-01#dreamhack +4
[๐Ÿฅ‡ Gold 4] ์ง€๊ทธ์žฌ๊ทธ๋กœ ์ ‘ํžŒ ํ”Œ๋ž˜๊ทธ โ€” DreamHack quilt ํ’€์ด
blog

[๐Ÿฅ‡ Gold 4] ์ง€๊ทธ์žฌ๊ทธ๋กœ ์ ‘ํžŒ ํ”Œ๋ž˜๊ทธ โ€” DreamHack quilt ํ’€์ด

๋ฐฐํฌ๋œ quilt๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ฒ€์‚ฌํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ์•„๋‹ˆ๋ผ ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ทธ๋ฆผ์œผ๋กœ ๊ตฝ๋Š” ์ธ์ฝ”๋”์˜€๋‹ค. /dev/urandom 192๋ฐ”์ดํŠธ ํ•œ๋ณตํŒ์— ์ธ์ž๋ฅผ ๊ทธ๋Œ€๋กœ ์–น๊ณ , 6๋น„ํŠธ์”ฉ ์ž˜๋ผ 64์ƒ‰ ํŒ”๋ ˆํŠธ๋กœ 32ร—32 ๋ธ”๋ก์„ ์น ํ•œ๋‹ค. ๋˜๋Œ๋ฆฌ๋Š” ๊ธธ๋ชฉ์— ๋†“์ธ ํ•จ์ •์€ ํ™€์ˆ˜ ๊ฒฉ์žํ–‰์„ ์ขŒ์šฐ๋กœ ๋’ค์ง‘๋Š” ํ•œ ์ค„์ด๋‹ค.
#dreamhack#ctf#reversing+4
2026-08-20#dreamhack +3