Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
Shelter — ROPベースのスリープ難読化によるメモリスキャナーの回避 | Kitploit
ツール/GitHubGitHub/kudaes/shelter
エクスプロイトフレームワークメモリフォレンジックレッドチーミング
GitHubkudaes/shelter

Shelter

ROPベースのスリープ難読化によるメモリスキャナーの回避

リポジトリを見る
390481年前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

Shelter

Shelterは、完全に武器化されたスリープ難読化手法であり、ROPを多用してインメモリペイロードを完全に暗号化することができます。

このクレートには以下の特徴があります:

  • AES-128暗号化。
  • PE全体の暗号化機能。
  • スリープ中の実行権限の削除。
  • APC/HWBP/タイマーは使用せず、難読化のためにROPのみを使用。
  • コールスタックスプーフィングを実現するためにUnwinderを使用し、ROPチェーンを実行。
  • 様々な状況に適応するための異なる実行方法。
  • その他のOPSEC対策:DInvoke_rs、間接システムコール、文字列リテラルの暗号化など。

Crates.io License

目次

  • 使い方
  • 例
    • fluctuate
    • fluctuate_from_address
    • fluctuate_from_pattern
  • モジュールのテスト
  • TO-DO

使い方

このクレートをプロジェクトにインポートするには、cargo.tomlに以下の行を追加します:

root@kitploit:~
[dependencies]
shelter = "=0.1.2"

その後、プロジェクトを--releaseモードでコンパイルします。

このクレートの主な機能は、次の3つの関数にラップされています:

  • fluctuate() - 現在のメモリ領域またはPE全体を暗号化します。この関数は、ベースアドレスを動的に取得するためにPEのMZバイトが存在する必要があります。
  • fluctuate_from_address() - PE全体を完全に暗号化します。この関数は、入力パラメータとしてPEのベースアドレスを期待します。
  • fluctuate_from_pattern() - PE全体も完全に暗号化します。この関数は、入力パラメータとして、PEのベースアドレスを特定するためのカスタム2バイトセットを期待します。このカスタムマジックバイトは、従来のMZパターンを置き換えます。

PE全体が暗号化されると、元のセクションのメモリ保護がヒープに保存され、後で復元されます。

ShelterはスリープのためにNtWaitForSingleObjectを使用します。スリープ時間(秒)を指定するだけでなく、イベントハンドルを渡し、任意のタイミングでシグナルを送ることでタイムアウト前に復帰させることもできます(例えばSetEventを使用)。ペイロード全体が暗号化されている場合(おそらくそれが目的でしょう)、無制限にスリープする場合にはイベントをシグナルする別の方法が必要になることに注意してください。

例

fluctuate

この関数は以下のパラメータを期待します:

  • PE全体を暗号化するか、現在のメモリ領域のみを暗号化するかを示す真偽値。trueを渡す場合、メモリ内にMZバイトが存在する必要があります。
  • プログラムがスリープする秒数。Noneにするとタイムアウトは無限になり、NtWaitForSingleObjectに渡されたイベントがシグナルされるまで実行は戻りません。
  • NtWaitForSingleObjectに渡すイベントハンドル。このパラメータはNoneにできます。このパラメータとタイムアウトの両方をNoneにするとプログラムは停止します。
root@kitploit:~
let time_to_sleep = Some(10); // 10秒間スリープ
let _ = shelter::fluctuate(false, time_to_sleep, None); // 現在のメモリ領域のみを暗号化
root@kitploit:~
let time_to_sleep = Some(10); // 10秒間スリープ
let _ = shelter::fluctuate(true, time_to_sleep, None); // PE全体を暗号化
root@kitploit:~
pub type CreateEventW = unsafe extern "system" fn (*const SECURITY_ATTRIBUTES, i32, i32, *const u16) -> HANDLE;

let k32 = dinvoke_rs::dinvoke::get_module_base_address("kernel32.dll"); 
let create_event: CreateEventW;
let event_handle: Option<HANDLE>;
dinvoke_rs::dinvoke::dynamic_invoke!(k32,"CreateEventW",create_event,event_handle,ptr::null_mut(),0,0,ptr::null());
let time_to_sleep = None; // 無制限にスリープ
let _ = shelter::fluctuate(true, time_to_sleep, event_handle); // イベントがシグナルされるまでPE全体を暗号化

fluctuate_from_address

この関数は以下のパラメータを期待します:

  • プログラムがスリープする秒数。Noneにするとタイムアウトは無限になり、NtWaitForSingleObjectに渡されたイベントがシグナルされるまで実行は戻りません。
  • NtWaitForSingleObjectに渡すイベントハンドル。このパラメータはNoneにできます。このパラメータとタイムアウトの両方をNoneにするとプログラムは停止します。
  • PEがマッピングされているベースアドレス。

この関数を使用する方法の1つは、Dinvoke_rsでペイロードを手動マッピングすることです。これにより、ローダーはペイロードに自身のベースアドレスを送信できるため、ペイロードは必要に応じて自身を難読化できます。この方法により、ローダーは特定のステルス性を達成するためにPEのヘッダーを安全に削除できます。

ローダーの例:

root@kitploit:~
let payload: Vec<u8> = your_download_function();
let mut m = dinvoke_rs::manualmap::manually_map_module(payload.as_ptr(), true).unwrap();
println!("The dll is loaded at base address 0x{:x}", m.1);
let dll_exported_function = dinvoke::get_function_address(m.1, "run");

let run: unsafe extern "Rust" fn (usize) = std::mem::transmute(dll_exported_function);
run(m.1 as usize);

ペイロードの例:

root@kitploit:~
#[no_mangle]
fn run(base_address: usize)
{
	...
	let time_to_sleep = Some(10); // 10秒間スリープ
	let _ = shelter::fluctuate_from_address(time_to_sleep, None, base_address); // この特定のベースアドレスからPE全体を暗号化
	...
}

fluctuate_from_pattern

この関数は以下のパラメータを期待します:

  • プログラムがスリープする秒数。Noneにするとタイムアウトは無限になり、NtWaitForSingleObjectに渡されたイベントがシグナルされるまで実行は戻りません。
  • NtWaitForSingleObjectに渡すイベントハンドル。このパラメータはNoneにできます。このパラメータとタイムアウトの両方をNoneにするとプログラムは停止します。
  • PEのベースアドレスを取得するために検索するカスタムマジックバイトを含む[u8;2]配列。

この関数を作成した目的は、ローダーがPEのヘッダーや従来のMZバイトを含むその他のシグネチャを削除できるようにすることです。これにより、それらのバイトをShelterがPEのベースアドレスを取得するために検索するカスタムパターンに置き換えることができます。

root@kitploit:~
let time_to_sleep = Some(10); // 10秒間スリープ
let pattern = [0x29,0x07];
let _ = shelter::fluctuate_from_pattern(time_to_sleep, None, pattern); // カスタムパターンをマジックバイトとして使用してPE全体を暗号化

モジュールのテスト

この技術の実装をテストするために、主にPE-sieveが使用されています。デフォルトでは、PE-sieveは実行可能メモリ領域内のインプラントを探します。つまり、現在のメモリ領域(.text)のみを難読化するだけでも検出を回避するのに十分です:

現在のメモリ領域の難読化。 現在のメモリ領域の難読化(Process Hacker)。

Unwinderを使用しているため、コールスタックが偽装されており、そのため/threadsフラグでもマッピングされたDLLは検出されません。

さて、PE-sieveでは/dataフラグを使用して非実行可能メモリ領域も検査できます。ツールの公式ドキュメントによると、このフラグをalwaysに設定すると「多くのノイズや誤検出が発生する可能性があります」。それにもかかわらず、PE全体の暗号化機能の有効性を確認するためにこのフラグを使用することにしました。これは、インメモリインプラントの存在を示す可能性のあるPEのデータ領域(MZ、DOSヘッダー、セクション名など)を隠すことができるからです。

PE-sieveによって検出された現在のメモリ領域の難読化。 検出されないPE全体の難読化。

見ての通り、最初の画像では、PE-sieveが非実行可能メモリページをスキャンする場合、.textセクションのみを難読化するだけでは不十分であることが示されています。これは、一部の領域にDLLの存在を明らかにする文字列(MZ、DOSヘッダー、セクション名など)が含まれている可能性があるためです。一方、2番目の画像では、ShelterのPE全体の難読化メカニズムを使用することでこの問題を解決できることが示されています。ただし、PE-sieveのwikiにも記載されているように、このオプションは大量の誤検出を引き起こします。なぜなら、ヒープ内に".data"や"rdata"などの文字列が存在するだけでも、実際のPEコンテンツがその領域に存在しないにもかかわらず、インプラントされたPEの可能性を警告するからです(その領域から何かをダンプすることはできませんが)。

最後に、PE-sieveには、高エントロピーのメモリ領域を探すことで難読化されたインプラントを検出する比較的新しいオプションがあります。このオプション(/obfusc)と/dataを組み合わせると、ペイロードを含むメモリ領域の高エントロピーによりペイロードの存在を検出できます(ただし、完全に暗号化されているためPEを取得することはできません):

PE全体の難読化の検出。 PE全体の難読化(Process Hacker)。

TO-DO

Shelterはすぐに使用でき、OPSECを考慮して開発されていますが、近い将来に追加される予定のいくつかの改良点があります:

  • PE全体が暗号化されたときのエントロピーを低減する。
  • BCryptEncrypt/BCryptDecryptを対応するNt関数に置き換える。
  • ガジェット選択プロセスにランダム性を追加する。

参考にした先駆的な研究

  • Gargoyle
  • Ekko
  • Cronos
ツールをダウンロード