Skip to content
このページは AI の支援により作成・翻訳されました。誤りがあれば、改善にご協力ください。 GitHub で編集

WASMプラグイン開発

PRX-SDにはWasmtimeを搭載したプラグインシステムが含まれており、WebAssembly(Rust、Go、C、AssemblyScriptなど)にコンパイルされる任意の言語で書かれたカスタムスキャナーで検出エンジンを拡張できます。プラグインは設定可能なリソース制限を持つサンドボックス化されたWASM環境で実行されます。

アーキテクチャ

~/.prx-sd/plugins/
  my-scanner/
    plugin.json          # Plugin manifest
    my_scanner.wasm      # Compiled WASM module
  another-plugin/
    plugin.json
    another_plugin.wasm

スキャンエンジンが起動すると、PluginRegistryはプラグインディレクトリを走査し、plugin.jsonを含むすべてのサブディレクトリを読み込み、WASMモジュールをコンパイルして、プラグインのon_loadエクスポートを呼び出します。スキャン中、現在のファイルにfile_typesplatformsが一致する各プラグインが順次呼び出されます。

実行フロー

  1. 探索 -- PluginRegistry~/.prx-sd/plugins/内のplugin.jsonファイルを検索
  2. コンパイル -- Wasmtimeがfuelメータリングとメモリ制限付きで.wasmモジュールをコンパイル
  3. 初期化 -- on_load()が呼び出され、plugin_name()plugin_version()が読み取られる
  4. スキャン -- 各ファイルに対して、scan(ptr, len) -> scoreがファイルデータと共に呼び出される
  5. レポート -- プラグインはreport_finding()を呼び出して脅威を登録するか、非ゼロスコアを返す

プラグインマニフェスト(plugin.json)

すべてのプラグインディレクトリには、プラグインとそのサンドボックス制約を説明するplugin.jsonが必要です:

json
{
  "name": "Example Scanner",
  "version": "0.1.0",
  "author": "prx-sd",
  "description": "Example plugin that detects MALICIOUS_MARKER string",
  "wasm_file": "example_plugin.wasm",
  "platforms": ["all"],
  "file_types": ["all"],
  "min_engine_version": "0.1.0",
  "permissions": {
    "network": false,
    "filesystem": false,
    "max_memory_mb": 64,
    "max_exec_ms": 5000
  }
}

マニフェストフィールド

フィールドタイプ必須説明
namestringはい人間が読めるプラグイン名
versionstringはいプラグインのセマンティックバージョン
authorstringはいプラグインの作者または組織
descriptionstringはいプラグインが検出するものの簡単な説明
wasm_filestringはいコンパイルされたWASMモジュールのファイル名(プラグインディレクトリからの相対パス)
platformsstring[]はいターゲットプラットフォーム:"linux""macos""windows"、または"all"
file_typesstring[]はい検査するファイルタイプ:"pe""elf""macho""pdf"、または"all"
min_engine_versionstringはい必要なPRX-SDエンジンの最小バージョン
permissions.networkbooleanいいえプラグインがネットワークにアクセスできるか(デフォルト:false
permissions.filesystembooleanいいえプラグインがWASI経由でホストファイルシステムにアクセスできるか(デフォルト:false
permissions.max_memory_mbintegerいいえMiB単位の最大リニアメモリ(デフォルト:64
permissions.max_exec_msintegerいいえms単位の最大ウォールクロック実行時間(デフォルト:5000

必須WASMエクスポート

WASMモジュールは以下の関数をエクスポートする必要があります:

scan(ptr: i32, len: i32) -> i32

メインスキャンエントリポイント。ゲストメモリ内のファイルデータへのポインタと長さを受け取ります。0から100の脅威スコアを返します:

  • 0 = クリーン
  • 1-29 = 情報提供
  • 30-59 = 疑わしい
  • 60-100 = 悪意のある

memory

モジュールはホストがファイルデータを書き込み、結果を読み取れるようにmemoryとしてリニアメモリをエクスポートする必要があります。

オプションWASMエクスポート

エクスポートシグネチャ説明
on_load() -> i32() -> i32コンパイル後に1回呼び出される。成功時は0を返す。
plugin_name(buf: i32, len: i32) -> i32(i32, i32) -> i32プラグイン名をバッファに書き込む。実際の長さを返す。
plugin_version(buf: i32, len: i32) -> i32(i32, i32) -> i32プラグインバージョンをバッファに書き込む。実際の長さを返す。
alloc(size: i32) -> i32(i32) -> i32sizeバイトのゲストメモリを割り当てる。ポインタを返す。

プラグインが使用できるホスト関数

ホストは"env"名前空間でこれらの関数を提供します:

report_finding(name_ptr, name_len, score, detail_ptr, detail_len)

脅威の検出を報告します。1回のスキャン中に複数回呼び出すことができます。

  • name_ptr / name_len -- 脅威名文字列のポインタと長さ(例:"Trojan.Marker"
  • score -- 脅威スコア(0-100、クランプされる)
  • detail_ptr / detail_len -- 詳細文字列のポインタと長さ

log_message(level, msg_ptr, msg_len)

エンジンのトレーシングシステムにログメッセージを書き込みます。

  • level -- 0=trace、1=debug、2=info、3=warn、4=error
  • msg_ptr / msg_len -- メッセージ文字列のポインタと長さ

get_file_path(buf_ptr, buf_len) -> actual_len

スキャンされているファイルのパスをゲストバッファに読み込みます。

get_file_type(buf_ptr, buf_len) -> actual_len

検出されたファイルタイプ(例:"pe""elf""pdf")をゲストバッファに読み込みます。

PluginFinding構造体

プラグインが検出を報告すると(report_finding()経由または非ゼロスコアを返すことで)、エンジンはPluginFindingを作成します:

rust
pub struct PluginFinding {
    pub plugin_name: String,   // Name of the plugin
    pub threat_name: String,   // e.g. "Trojan.Marker"
    pub score: u32,            // 0-100
    pub detail: String,        // Free-form detail string
}

プラグインが非ゼロスコアを返してもreport_finding()を呼び出さない場合、エンジンは自動的に検出を合成します:

threat_name: "Plugin.<plugin_name>"
detail: "Plugin '<name>' returned threat score <score>"

開発ワークフロー

1. プラグインディレクトリの作成

bash
mkdir -p ~/.prx-sd/plugins/my-scanner

2. マニフェストの作成

bash
cat > ~/.prx-sd/plugins/my-scanner/plugin.json << 'EOF'
{
  "name": "My Custom Scanner",
  "version": "0.1.0",
  "author": "your-name",
  "description": "Detects custom threat patterns",
  "wasm_file": "my_scanner.wasm",
  "platforms": ["all"],
  "file_types": ["all"],
  "min_engine_version": "0.1.0",
  "permissions": {
    "network": false,
    "filesystem": false,
    "max_memory_mb": 64,
    "max_exec_ms": 5000
  }
}
EOF

3. プラグインの作成(Rust例)

新しいRustライブラリプロジェクトを作成:

bash
cargo new --lib my-scanner
cd my-scanner

Cargo.tomlに追加:

toml
[lib]
crate-type = ["cdylib"]

[profile.release]
opt-level = "s"
lto = true

src/lib.rsを作成:

rust
// Host function imports
extern "C" {
    fn report_finding(
        name_ptr: *const u8, name_len: u32,
        score: u32,
        detail_ptr: *const u8, detail_len: u32,
    );
    fn log_message(level: u32, msg_ptr: *const u8, msg_len: u32);
}

#[no_mangle]
pub extern "C" fn on_load() -> i32 {
    let msg = b"My Custom Scanner loaded";
    unsafe { log_message(2, msg.as_ptr(), msg.len() as u32) };
    0 // success
}

#[no_mangle]
pub extern "C" fn scan(ptr: *const u8, len: u32) -> i32 {
    let data = unsafe { core::slice::from_raw_parts(ptr, len as usize) };

    // Example: look for a known malicious marker
    let marker = b"MALICIOUS_MARKER";
    if data.windows(marker.len()).any(|w| w == marker) {
        let name = b"Custom.MaliciousMarker";
        let detail = b"Found MALICIOUS_MARKER string in file data";
        unsafe {
            report_finding(
                name.as_ptr(), name.len() as u32,
                85,
                detail.as_ptr(), detail.len() as u32,
            );
        }
        return 85;
    }

    0 // clean
}

4. WASMへのコンパイル

bash
rustup target add wasm32-wasip1
cargo build --release --target wasm32-wasip1
cp target/wasm32-wasip1/release/my_scanner.wasm ~/.prx-sd/plugins/my-scanner/

5. プラグインのテスト

bash
# Create a test file with the marker
echo "MALICIOUS_MARKER" > /tmp/test-marker.txt

# Scan with debug logging to see plugin activity
sd --log-level debug scan /tmp/test-marker.txt

TIP

プラグインの読み込みと実行の詳細なメッセージ(fuelの消費量やメモリ使用量を含む)を確認するには--log-level debugを使用してください。

サンドボックスセキュリティ

プラグインは以下の制約を持つWasmtimeサンドボックス内で実行されます:

制約実施方法
メモリ制限マニフェストのmax_memory_mb;WasmtimeがリニアメモリのキャップをEnforce
CPU制限max_exec_msをfuelユニットに変換;fuelが切れたら実行を停止
ネットワークデフォルトで無効;permissions.network: trueが必要
ファイルシステムデフォルトで無効;permissions.filesystem: trueが必要(WASIプリオープン)
プラットフォームチェックplatformsが一致しないプラグインはロード時にスキップ
ファイルタイプフィルターfile_typesが一致しないプラグインはファイルごとにスキップ

WARNING

network: trueまたはfilesystem: trueの場合でも、WASIサンドボックスは特定のディレクトリやエンドポイントへのアクセスを制限します。これらの権限は意図の宣言であり、無制限のアクセス付与ではありません。

ホットリロード

新しいプラグインディレクトリを~/.prx-sd/plugins/にドロップすると、次のスキャンでレジストリがそれを検出します。デーモンの場合はsd updateを呼び出すかデーモンを再起動することでリロードをトリガーできます。

次のステップ

Released under the Apache-2.0 License.