有名Webサイトで使っている Wasmを読んでみる話 2025.06.25 こーのいけ
whoami • こーのいけ • X: ko_noike • Github/zenn/etc.: kounoike • 色々やってるフリーランスエンジニア • WebRTC/画像処理/映像処理/IoT
今日の話 • 主にWasmのブラウザ利用のお話 • 世の中の有名サイトはWasmをどう使っている? • Zoom/Figma/Google(Meet, スプレッドシート, Google Earth)と見てみるが…? • Wasmとして機能提供するとどこまでバレるのか? • 今回は簡単に読んでみる程度 • 対策は?
第三者のWasmを読む • .wasmファイルはブラウザでダウンロード される • バイナリ形式⇔テキスト形式(wasm2wat) • (対策してなければ)ある程度はそのまま 読める • 関数名などのシンボル • リテラル文字列
第三者のWasmを使う(技術的な話) Webサイト • HTML • CSS JSランタイム • JS function hoge() • WASM WebAssembly.Instanciate 第三者の.wasmを持ってきても、 周辺(import/export)を整えれば動く! 使い方はJSを(頑張って)読めば分 かる! (かなり大変ではあるので気軽に出来る ことではないが・・・) Module - Import - Export WebAssembly インスタンス fn hoge() 線形メモリ Uint8Array
デコンパイラ(github:WebAssembly/wabt wasm2c) • 雑なWebAssemblyで確認してみる • 適当にフィボナッチ数列(なぜかfloat) →wasm2c • 837行の.c、45行の.hが出てくる • まあ頑張れば読める…かな?
Zoom(ブラウザ版)のWasmを読んでみる Chromeの開発者ツールを開いた状態でZoomにブラウザ版でアクセス→Networkタブをwasmでフィルタ 最近のChromeなら選択してResponseタブで wat形式が読める場合もある(色々条件あり)
Zoom(ブラウザ版)→audio.simd.wasm • (名前からして)SIMDを使った音声処理機能 • import “env” “empscripten_asm_const_int”などがあるのでC/C++/Emscripten • exportしている関数名から以下のような機能がありそう • AEC(Acoustic Echo Cancellation)関係 • Denoise(雑音除去)関係 • QOS関係 • Volume関係
Zoom(ブラウザ版)→video.mtsimd.wasm • (名前からして)マルチスレッド化されたSIMD対応の映像機能 • Importしてる関数にemscripten_asm_intなどがあるのでC/C++/Emscripten • import “wasi_snapshot_preview1” “environ_get”などがあるのでちょっと新しめ? • Importしている関数群にDecoder/Encoderとあるので、JSのWebCodecsと連携して映像エ ンコードしているっぽい • Importにzlt_tfjs_initなどがあるので、github.com/tensorflow/tfjs と連携してるっぽい • Exportに_Video_VirtualBackground_Special_Actionがあるので、tfjs使って背景ぼかしを実 現してるっぽい • Exportに_Jpeg_(Init|Decode|Uninit)などがあるが、これは何だろう…? • E2EEっぽいものもある? • QOSや帯域制御っぽいものもある
Zoom(ブラウザ版)→tp.wasm • 他2つと違いサービスワーカーで読み込まれている小さなWasm • シンボルが難読化されていて簡単には分からない…
Figma 難読化されてる なぜかブラウザでもwasm2watで読めない ブラウザでは読める(wasm2watで読めない) 変な名前→何かバンドラーで細工している? 26b9~.wasmの内容 • すべての関数がnapi_* となっていて、npm:napi-wasmを使ったRust製っぽい • ちょっとnapiの使い方分かってないので簡単には解析できず…
Google Meet • どちらも難読化されてて簡単には分からない • 名前からすると音声解析と背景処理(mediapipe) • 背景処理が使うモデルファイルは昔はそのままダウンロードしてたのでtfliteで読み込めたが、 今は難読化されている • とはいえ、(理論上は|かなり頑張れば)解読可能
Google スプレッドシート • wasm2watで読めない… • stringsで文字列を探して読むと”j2wasm”という文字列が見つかる→github:google/j2cl • Javaらしい
Google Earth • canvaskit.wasmをstringsで見てるとskgpuという文字列があったので、 github:google/skiaっぽい • earthplugin_web.wasmは.proto2.という文字列が多いのでprotobuf処理してる? • 一部難読化されてないシンボルが見えて、C++/emscriptenっぽい感じ
Wasmの難読化を考える • Wasmの難読化は結構大変 • 対象 • リテラル値 • コード JSランタイム • Wasm内の関数 function hoge() • ImportされたJS関数 • ExportされたWasm関数 • Import/ExportはJS/Wasm両方で マッピングが必要 • TS/JS minify/バンドラーとの連携 Module - Import - Export WebAssembly インスタンス fn hoge() 線形メモリ Uint8Array
Wasmの難読化ツールの現状 • OSSのツールはいくつかあるが開発があまり活発ではない • 商用ツールはちょっと見つからず… ⇔Googleなどが使ってる事実??? • 多分、単体の難読化ツールとしてより、JSの難読化ツール(コード部分も難読化するヤツ)が 一緒にWasmも対応することになる (Googleなどが使ってる難読化がどこまで難読化しているかは未確認)
Wasm難読化以外のアプローチ • WebAssembly.instantiate()はWebAssemblyバイナリーコードが入力 →ラッパー・グルーコードがfetchして渡してる →Webに置いてあるwasmバイナリがwasmバイナリと分からないようにすれば良い? →fetch→デコード(ex: XOR/共通鍵暗号)→instantiate そもそもfetchじゃなくてJSにBase64などで埋め込みという手もあり →ただし、devtoolでプロファイル使えばコールスタックでWasm関数名がバレる
まとめ • WebサイトでWasmを使うと何も対策しないと色々見えてしまう • 主に関数名・リテラル・(場合によっては)AIモデルファイル →難読化が有効 • 難読化ツールのデファクトはまだ無い →デファクトツールのOriginal Authorになるチャンス かも