Webサイトを巡回・診断する際、難読化された JavaScript に遭遇することは珍しくありません。これまでは、難読化が判明した時点で深掘りを断念することが多かったのですが、生成AI を活用すれば難読化を解除できるのではないかと考え、検証してみました。
●検証に使ったもの
・JavaScript Obfuscator Tool
JavaScript の難読化ツールで有名な「javascript-obfuscator」を使いました。検証データも、公式Webサイト(https://obfuscator.io/)のものを使っています。
●検証手順
まず、公式Webサイトに用意されているシンプルなコードの例を難読化しました。(「Obfuscate」ボタンを押下)

その結果がこちらです。なんだか解読が難しそうな内容になっています。
function _0x15ac(){var _0x5e73f0=['2536116diIsZv','1xMcYLN','8469RqDtTi','1698rloQyd','19965rtDzro','356JrgCOZ','12817GotZLe','22NxUAtb','509460uuAjJN','2667832WgNaBX','4110ijmszv','746350PUVBOY','Hello\x20World!'];_0x15ac=function(){return _0x5e73f0;};return _0x15ac();}function _0x285a(_0x4bb21f,_0x32e756){var _0x15acad=_0x15ac();return _0x285a=function(_0x285ad9,_0x2aac56){_0x285ad9=_0x285ad9-0x1f0;var _0x1fa365=_0x15acad[_0x285ad9];return _0x1fa365;},_0x285a(_0x4bb21f,_0x32e756);}(function(_0x1a4cb8,_0x138389){var _0x1bde3f=_0x285a,_0x2c1dfd=_0x1a4cb8();while(!![]){try{var _0x2a6937=-parseInt(_0x1bde3f(0x1f6))/0x1*(parseInt(_0x1bde3f(0x1f0))/0x2)+parseInt(_0x1bde3f(0x1f9))/0x3*(parseInt(_0x1bde3f(0x1fa))/0x4)+-parseInt(_0x1bde3f(0x1f3))/0x5+parseInt(_0x1bde3f(0x1f8))/0x6*(parseInt(_0x1bde3f(0x1fb))/0x7)+-parseInt(_0x1bde3f(0x1f1))/0x8+parseInt(_0x1bde3f(0x1f7))/0x9*(-parseInt(_0x1bde3f(0x1f2))/0xa)+-parseInt(_0x1bde3f(0x1fc))/0xb*(-parseInt(_0x1bde3f(0x1f5))/0xc);if(_0x2a6937===_0x138389)break;else _0x2c1dfd['push'](_0x2c1dfd['shift']());}catch(_0x599273){_0x2c1dfd['push'](_0x2c1dfd['shift']());}}}(_0x15ac,0x63d5c));function hi(){var _0x399507=_0x285a;console['log'](_0x399507(0x1f4));}hi();
次に、この難読化されたコードを ChatGPT に解読してもらいました。
私:
------------------------------------------------------------------------------------------------------------
以下は難読化された JavaScript です。難読化を解除してもらえますか?
function _0x15ac(){var _0x5e73f0=['2536116diIsZv','1xMcYLN','8469RqDtTi','1698rloQyd','19965rtDzro','356JrgCOZ','12817GotZLe','22NxUAtb','509460uuAjJN','2667832WgNaBX','4110ijmszv','746350PUVBOY','Hello\x20World!'];_0x15ac=function(){return _0x5e73f0;};return _0x15ac();}function
– 略 –
------------------------------------------------------------------------------------------------------------
ChatGPT:
------------------------------------------------------------------------------------------------------------
この難読化された JavaScript を解除すると、以下のようになります。
function hi() {
console.log("Hello World!");
}
hi();
解除したコードを簡単に説明すると、hi関数は "Hello World!" という文字列をコンソールに出力するだけのものです。難読化された部分を取り除くと、実際には次のようなシンプルなコードになります。
function hi() { console.log("Hello World!"); } hi();
------------------------------------------------------------------------------------------------------------
正解です。あまりにも簡単に解除できたので、ChatGPT が「学習済みのデータを出力しているだけではないか」という疑念が浮かびました。
そこで、難読化するコードの難易度をあげて「フォームに入力された値を取得し、それを Fetch API でサーバーに送信するコード」に変更し、再度同じ手順で検証してみました。
変更後のコード

私:
------------------------------------------------------------------------------------------------------------
以下は難読化された JavaScript です。難読化を解除してもらえますか?
const _0x2c7fe7=_0x5143;(function(_0x39b28c,_0x1ae5c3){const _0x5934b5=_0x5143,_0xf4c7ff=_0x39b28c();while(!![]){try{const _0x11f21e=parseInt(_0x5934b5(0x16d))/0x1+-parseInt(_0x5934b5(0x177))/0x2*(parseInt(_0x5934b5(0x175))/0x3)+parseInt(_0x5934b5(0x176))/0x4+-parseInt(_0x5934b5(0x171))/0x5+parseInt(_0x5934b5(0x168))/0x6*(parseInt(_0x5934b5(0x17b))/0x7)+parseInt(_0x5934b5(0x165))/0x8*(-parseInt(_0x5934b5(0x174))/0x9)+parseInt(_0x5934b5(0x169))/0xa*(parseInt(_0x5934b5(0x170))/0xb);if(_0x11f21e===_0x1ae5c3)break;else _0xf4c7ff['push'](_0xf4c7ff['shift']());}catch(_0xdfa6ca){_0xf4c7ff['push'](_0xf4c7ff['shift']());}}}(_0x35d4,0xf35e1));function _0x5143(_0x4443dc,_0x565d8c){const _0x35d415=_0x35d4();return _0x5143=function(_0x51438c,_0x4535b9){_0x51438c=_0x51438c-0x165;let _0x13db52=_0x35d415[_0x51438c];return _0x13db52;},_0x5143(_0x4443dc,_0x565d8c);}function sendRequest(){const _0x53f1da=_0x5143,_0x4c7dfa=document[_0x53f1da(0x166)](_0x53f1da(0x16a))[_0x53f1da(0x17a)];fetch('https://example.com/api',{'method':_0x53f1da(0x172),'headers':{'Content-Type':'application/json'},'body':JSON[_0x53f1da(0x16b)]({'value':_0x4c7dfa})})[_0x53f1da(0x178)](_0x558130=>_0x558130['json']())[_0x53f1da(0x178)](_0x45d2b6=>{const _0x46ff3a=_0x53f1da;console[_0x46ff3a(0x173)](_0x46ff3a(0x16c),_0x45d2b6);})[_0x53f1da(0x179)](_0xc2feb8=>{const _0x359681=_0x53f1da;console[_0x359681(0x167)]('Error:',_0xc2feb8);});}function _0x35d4(){const _0x47a54e=['2732224WelyXt','589282YmIBCX','then','catch','value','14fjeVsV','6296XqOTaY','getElementById','error','322320wIfxQF','17758860yEJFxm','inputField','stringify','Success:','485149hEDJub','submitButton','click','22SwWSyM','9412280oHJhqL','POST','log','18909toivbG','3HKqBcK'];_0x35d4=function(){return _0x47a54e;};return _0x35d4();}document[_0x2c7fe7(0x166)](_0x2c7fe7(0x16e))['addEventListener'](_0x2c7fe7(0x16f),sendRequest);
------------------------------------------------------------------------------------------------------------
ChatGPT:
------------------------------------------------------------------------------------------------------------
このコードは難読化されていますが、元のコードに戻すことができます。以下が難読化を解除した JavaScriptコードです:

------------------------------------------------------------------------------------------------------------
なんと、コメント部分以外は完全に復元されました。コメント部分は難読化時に欠落したと考えられることから、正解といえます。
ChatGPT は、解除のプロセスを以下のように説明しました。
ChatGPT:
------------------------------------------------------------------------------------------------------------
難読化された JavaScriptコードを解除するプロセスを、以下のステップで行いました。
1. 難読化コードの確認
・提供されたコードをまず全体的に確認し、難読化されているパターンを理解しました。このコードでは、配列と関数を使ってコードを混乱させています。
2. 文字列参照の解除
・難読化コードには、_0x5143 という関数があり、これが文字列の参照に使用されていました。この関数の役割は、配列のインデックスを計算して対応する文字列を取得することです。
・_0x5143 関数が行っているのは、内部的に配列 _0x35d4() の内容を参照して、対応する文字列を返すことです。この関数の構造を理解し、次に実際に使われている文字列を復元しました。
3. 文字列の置換
・sendRequest 関数の中で使用されている文字列の参照部分を、実際の文字列に置換しました。例えば、_0x5143(0x166) は getElementById であり、これをそのまま置き換えました。
4. 配列の解釈
・_0x35d4() 関数は、難読化に使用されている配列を生成する関数です。この関数を実行して得られる配列の内容から、コード内の各文字列参照がどの文字列に対応しているかを確認しました。
5. 難読化解除後のコード整理
・すべての文字列参照を置換した後、元のコードのロジックを明確にするために、コードを整理しました。この段階で、コードがどのように動作するのかを正確に理解できるようになりました。
6. 動作確認
・解除したコードが、元の難読化コードと同じ動作をすることを確認しました。これにより、解除が正確に行われたことを確認します。
実施した具体的な置換例:
・_0x5143(0x166) → 'getElementById'
・_0x5143(0x16a) → 'inputField'
・_0x5143(0x16b) → 'stringify'
・_0x5143(0x172) → 'POST'
・_0x5143(0x178) → 'then'
・_0x5143(0x179) → 'catch'
・_0x5143(0x173) → 'log'
・_0x5143(0x167) → 'error'
これらのステップを通じて、難読化コードを元の形に戻しました。難読化されたコードを解除するのは手間がかかる作業ですが、パターンを理解し、システマティックに置換していくことで解除が可能です。
------------------------------------------------------------------------------------------------------------
ChatGPT は、配列の内容を解析し、元のコードを復元する手順を説明しました。コードの内容を完全に理解した上で解除しているように見えます。非常に素晴らしい結果です。
●High設定での難読化
ここまでは javascript-obfuscator のデフォルト設定である Medium を用いて検証していましたが、最後におまけとして、Options Preset で High を選択して実行してみました。この設定では、より複雑な難読化が行われます。
難読化後のコードは以下の通りで、もはや何が書いてあるのか全く分かりません。
```
(function(_0x5dc5fd,_0x16b5c9){function _0x366c4d(_0x55b52f,_0x33eb68,_0x1e862e,_0xb34009,_0x26eaee){return _0xf551(_0xb34009-0x1bf,_0x1e862e);}function _0x5e6eb8(_0x4283c7,_0x15968c,_0x4bd59e,_0x32c7e0,_0x2cb6df){return _0xf551(_0x15968c- -0x135,_0x4283c7);}const _0x369c4f=_0x5dc5fd();function _0x164376(_0x3a2f75,_0x452577,_0x3b954f,_0x21a904,_0x3a39e3){return _0xf551(_0x3b954f-0x1ff,_0x3a2f75);}function _0x137060(_0x3e7350,_0x4e3bd9,_0x15386e,_0xcfe28a,_0x263cac){return _0xf551(_0xcfe28a- -0x2e3,_0x263cac);}function _0x202b69(_0x1ee378,_0x3544f9,_0x5ced51,_0xe6bc18,_0x238fe0){return _0xf551(_0x5ced51- -0x288,_0x238fe0);}while(!![]){try{const _0x345c14=-parseInt(_0x366c4d(0x3ca,0x48c,'u%BP',0x467,0x4a4))/(0x1639+0x1d87+-0x33bf)*(parseInt(_0x366c4d(0x40d,0x51c,'N3YB',0x4ee,0x550))/(-0x530+-0xa3c+0x9e*0x19))– – 略 –
```
結果、こちらは元のコードを復元することができませんでした。ただし、Medium設定であれば復元できたため、Medium が現在の ChatGPT に解読可能な限界と考えられます。
●まとめ
今回の検証により、JavaScript の難読化は設定次第で解除可能であることがわかりました。生成AI は驚異的な速度で進化しており、特にコーディングの分野では、人間に匹敵する性能を発揮するまでに至っています。その使い方次第では、既存のセキュリティ対策を突破する手段として利用される可能性もあるため、その性能を理解し、常に最新の知識を取り入れていくことが重要です。
エーアイセキュリティラボでは、生成AI の活用に注力しており、AeyeScan への統合や、その他の製品への応用の可能性を日々研究しています。生成AI を活用した機能のトライアルも可能ですので、ご興味ある方はぜひお試しください。