ドキュメント
/
VM 難読化
/

直接 eval の挙動

直接 eval() による VM 難読化の無効化

関数の本体のどこか(ネストされた関数を含む)に直接の eval(code) 呼び出しがあると、その関数は VM 難読化の対象外になり、難読化ツールは VMDynamicCodeSkipped 警告を出力します(関数に名前がある場合はその名前が示されます)。root モード(デフォルト)ではその関数全体とネストされたすべての関数が対象外になります。comment モードでは、個別にマークしたネストされた関数は、それ自体が eval を含まない限り引き続き仮想化されます。静的でない引数を持つ直接 eval は、DynamicCodeRenameRisk 警告も出力します。直接 eval は外側の関数のローカル変数にアクセスできますが、関数を VM バイトコードにコンパイルするとそれらの変数は利用できなくなるためです。

IIFE でラップしたコードでは特に重要です。 トップレベルの IIFE の奥深くに直接 eval が 1 つでもあると、IIFE 全体(あなたのコードすべてを含む)が VM 難読化されません。

JavaScript

IIFE のラップを外すことにはトレードオフがあります。関数がトップレベルに置かれると、対象外になるのは問題のある関数だけになりますが、残りの関数はルートレベルになるため、VM 難読化はそれらの名前を保持します。それらの名前を出力に残さない方法については、LLM の解析から関数名を隠すを参照してください。

(0, eval)(code) や window.eval(code) のような間接的な形式は VM 難読化を妨げません。ただし eval?.(code) は例外です。JavaScript はこれを間接 eval として実行しますが、難読化ツールは保守的に直接 eval として扱い、その関数を対象外にします。

Function コンストラクター(new Function(body) / Function(body))も、本体の引数が動的な場合は同じように扱われます。 動的な new Function(...) 呼び出しを含む関数も、VMDynamicCodeSkipped 警告とともに対象外となります。動的な直接 eval の場合と同様に、実行時に組み立てられる本体がリネームされた識別子を参照する可能性があるため、難読化ツールは DynamicCodeRenameRisk 警告も追加します。new Function('a', 'b', 'return a + b') のような完全に静的な呼び出しは対象外になりません。

間接 eval と Function コンストラクターはグローバルスコープで実行されます。呼び出し元のローカル変数は読めませんが、リネームまたは削除されたグローバルを参照すると失敗することがあります。new Function('a', 'b', 'return a + b') のように自身のパラメーターだけを使う静的な本体であれば、その依存関係を避けられます。警告を確認し、最終的なバンドルをテストしてください。eval の構文を変えるだけでは、任意の動的コードが安全になるわけではありません。

回避策(v6.14.0 以降):vmForceCompileDynamicCode: true を設定する(または VM セクションの 上書き設定 グループにある Force Compile Dynamic Code スイッチをオンにする)と、それでも外側の関数をバイトコード化し、VMDynamicCodeSkipped を抑制します。スコープを修復することはできません。強制的にコンパイルされた関数の内部では、コードが文字列リテラルであっても、直接 eval はその関数のローカル変数、パラメーター、仮想化された外側の関数の変数を読み書きできません。評価されるコードがグローバル以外を一切参照しない場合にのみ使用してください。このオプションを有効にしても DynamicCodeRenameRisk は引き続き出力されます。この警告が示すリネームのリスクは、VM の対象外化とは無関係だからです。

全体の対応表、警告の形式、回避策については eval と new Function を含むコードの VM 難読化を参照してください。