๋ฌธ์ : 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
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"
1,984๋ฐ์ดํธ. ๊ฒ์ฆ๋ ํต๊ณผํ๋ค. ์ด์ ๋ถํฐ๋ ๋ธ๋ผ์ฐ์ ๋ฅผ ์๊ณ ๋ฐ์ด๋๋ฆฌ ํ๋๋ง ๋ณธ๋ค.
๐ฌ ์ ์ฐฐ โ ์น์ ๊ณผ export๋ถํฐ
wasm-objdump -h๋ก ์น์
๋ฐฐ์น๋ฅผ ๋ณธ๋ค.
./node_modules/wabt/bin/wasm-objdump -h passcode.wasm
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/muslwasmception์ด๋ผ๋ ์ด๊ธฐ 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 ์น์
์ ์์ ์๋ค](/_next/image?url=%2Fimages%2Fblog%2Fdreamhack-passcode-writeup%2F04_objdump_export.png&w=3840&q=75)
๊ทธ๋ฆฌ๊ณ 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
์ค๋ฌด ์ค์ด๋ค. ์ฎ๊ฒจ ์ ์ผ๋ฉด ์ด๋ ๋ค.
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
ํ ๋ฐ๋ณต์์ ์ผ์ด๋๋ ์ผ์ ์ ๋ฆฌํ๋ฉด ์ธ ๋จ๊ณ๋ค.

ํ์ ๋ฐฉํฅ์ด c์ ํ์ง์ผ๋ก ๊ฐ๋ฆฐ๋ค. e = c & 7, f = (0 - c) & 7์ด๋ผ e + f๊ฐ ํญ์ 8์ด๊ณ , ์ง์ ๋ฐ๋ณต์ ์ค๋ฅธ์ชฝ์ผ๋ก e๋งํผ, ํ์ ๋ฐ๋ณต์ ์ผ์ชฝ์ผ๋ก e๋งํผ ๋๋ ์ ํํ 8๋นํธ ํ์ ์ด๋ค. XOR ์งํ ๊ฐ์ด 0~255 ๋ฒ์๋ผ & 255 ๋ง์คํฌ์ ๋ง๋ฌผ๋ ค ์์ ๋นํธ๊ฐ ์์ง ์๋๋ค.
์ฌ๊ธฐ์ ๊ฒฐ์ ์ ์ธ ๊ฒ ํ๋ ๋์จ๋ค. ์ธ ์ฐ์ฐ ์ ๋ถ ๋ฐ์ดํธ ํ๋ ์์์ ๋๋๋ค. ์ ๋ฐ์ดํธ๋ฅผ ์ฝ์ง๋, ์ฐ์ง๋ ์๋๋ค. ๊ทธ๋ฌ๋ 25๋ฐ์ดํธ๋ฅผ ์ฐ๋ฆฝ๋ฐฉ์ ์์ผ๋ก ๋ฌถ์ด ํ ํ์๊ฐ ์๋ค.

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
6e00 0000 0000 0000 ์๋ฆฌ๊ฐ ๋ณด์ธ๋ค. target์ ๋ง์ง๋ง ๋ฐ์ดํธ 0x6e ๋ค๋ก 0์ด ์ผ๊ณฑ ๊ฐ. ์์ํ ํจ๋ฉ์ด๋ค.
node dump_tables.js
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
์ ๋ค 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
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
0์ด๋ค. ๋์กฐ๊ตฐ์ผ๋ก ์ฒซ ๊ธ์๋ง X๋ก ๋ฐ๊ฟจ๋๋ 202๊ฐ ๋์จ๋ค. ํ์ ์ด ์ค์ ๋ก ์
๋ ฅ์ ๋ฐ์ํ๋ค๋ ํ์ธ์ด๋ค.
์ฌ๊ธฐ๊น์ง ์์ผ๋ฉด ๋ธ๋ผ์ฐ์ ๋ก๋ ๋ด์ผ ํ๋ค. ์๋ฒ๊ฐ ์๋ ๋ฌธ์ ๋ผ extracted/ ํด๋๋ฅผ ๊ทธ๋๋ก ๋ก์ปฌ์์ ์๋นํ๋ค.
cd extracted && python3 -m http.server 8412 --bind 127.0.0.1
์ ๋ ฅ์นธ ํ๋๊ฐ ์ ๋ถ๋ค. ๋จผ์ ํ๋ฆฐ ๊ฐ์ ๋ฃ์ด ๋ณธ๋ค.

๊ทธ๋ฆฌ๊ณ ์ญ์ฐํ ๊ฐ.

๋ง์ง๋ง์ผ๋ก 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)
๋ด๋ณด๋ด๊ธฐ ๋ชฉ๋ก์ด 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
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ