Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
Unwinder — Rust用のコールスタックスプーフィング | Kitploit
ツール/GitHubGitHub/kudaes/unwinder
IDS/IPS回避ポストエクスプロイトレッドチーミングペイロード開発敵対的攻撃
GitHubkudaes/unwinder

Unwinder

Rust用のコールスタックスプーフィング

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

人気

すべて見る →

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

すべてのツールを探索

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

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

目次

  • SilentMoonWalk
    • 説明
    • クレジット
    • 使い方
      • call_function!() マクロ
      • indirect_syscall!() マクロ
      • パラメータの受け渡し
    • 例
      • kernel32.dll!Sleep() を呼び出す
      • kernel32.dll!OpenProcess() を呼び出す
      • NtDelayExecution() を間接システムコールとして呼び出す
      • マクロ呼び出しの連結
    • 注意事項
      • 最初のフレーム
      • PoC
  • スタック置換
    • 技術の説明
    • 使用方法
    • 例
    • 注意事項

SilentMoonWalk

説明

Unwinder は SilentMoonWalk 技術の完全な兵器化を提供し、Rust で完全かつ安定したコールスタックスプーフィングを実現します。

この技術には以下の特徴があります:

  • 最大11個のパラメータを持つ任意の関数を実行可能。
  • 最大11個のパラメータを持つ間接システムコールを(追加のヒープ割り当てなしで)実行可能。
  • このクレートは、それを通じて呼び出された関数が返す値を取得できます。
  • スプーフィング処理は、コールスタックのサイズを増やすことなく、何度でも連結できます。
  • TLS はスプーフィング処理中の効率を高めるために使用されます。
  • このクレートが必要とする Windows API 呼び出しには dinvoke_rs が使用されます。

クレジット

SilentMoonWalk 技術の作者に感謝します:

  • KlezVirus
  • Waldo-IRC
  • Trickster0

そしてもちろん、このプロジェクト全体にインスピレーションを与えた Twitter スレッド を公開した namazso に多大な感謝を捧げます。

使い方

このクレートをプロジェクトにインポートするには、cargo.toml に次の行を追加し、release モードでコンパイルしてください:

[dependencies]
unwinder = "=0.1.4"

このクレートの主な機能は2つのマクロにまとめられています:

  • call_function!() マクロは、クリーンなコールスタックで任意の関数を実行できます。
  • indirect_syscall!() マクロは、指定された(間接)システムコールをクリーンなコールスタックで実行します。

これらのマクロを使用するには、std::ffi::c_void データ型をインポートする必要があります。

どちらのマクロも *mut c_void を返し、実行された関数が返す値を取得するために使用できます。詳細は例のセクションを参照してください。

call_function!() マクロ

このマクロは、クリーンなコールスタックで任意の関数を呼び出すために使用します。 このマクロは以下のパラメータを期待します:

  • 1つ目のパラメータは、コールスタックをスプーフィングした後に呼び出すメモリアドレスです。このパラメータは usize、isize、またはポインタとして渡す必要があります。
  • 2つ目のパラメータは、開始関数のフレームを保持するかどうかを示す bool です。不明な場合は false に設定してください。常に良好なコールスタックが保証されます。
  • 以降のパラメータは、コールスタックがスプーフィングされた後に関数に渡す引数です。

indirect_syscall!() マクロ

このマクロは、クリーンなコールスタックで任意の間接システムコールを実行するために使用します。 このマクロは以下のパラメータを期待します:

  • 1つ目のパラメータは、システムコールを実行したい NT 関数の名前を含む文字列です。
  • 2つ目のパラメータは、開始関数のフレームを保持するかどうかを示す bool です。不明な場合は false に設定してください。常に良好なコールスタックが保証されます。
  • 以降のパラメータは、NT 関数に渡す引数です。

パラメータの受け渡し

これら2つのマクロにさまざまな型の引数を渡すには、以下の点に注意する必要があります:

  • usize に変換できる任意の基本データ型(u8-u64、i8-i64、bool など)は、マクロに直接渡すことができます。
  • サイズが8、16、32、64 ビットの構造体と共用体は、同じサイズの整数として渡されます。
  • サイズが64 ビットを超える構造体と共用体は、ポインタとして渡す必要があります。
  • 文字列(&str と String)は、ポインタとして渡す必要があります。
  • NULL ポインタ(ptr::null()、ptr::null_mut() など)は 0 として渡されます(u8、u16、i32 など、どの型でも同じです)。
  • 浮動小数点および倍精度のパラメータは現在サポートされていません。
  • その他のデータ型はすべてポインタとして渡す必要があります。

例

kernel32.dll!Sleep() を呼び出す

let k32 = dinvoke_rs::dinvoke::get_module_base_address("kernel32.dll");
let sleep = dinvoke_rs::dinvoke::get_function_address(k32, "Sleep"); // Memory address of kernel32.dll!Sleep() 
let miliseconds = 1000i32;
unwinder::call_function!(sleep, false, miliseconds);

kernel32.dll!OpenProcess() を呼び出す

let k32 = dinvoke_rs::dinvoke::get_module_base_address("kernel32.dll"); 
let open_process: isize = dinvoke_rs::dinvoke::get_function_address(k32, "Openprocess");
let desired_access: u32 = 0x1000;
let inherit = 0i32;
let pid = 20628i32;
let handle = unwinder::call_function!(open_process, false, desired_access, inherit, pid); // returns *mut c_void
let handle: HANDLE = std::mem::transmute(handle);
println!("Handle id: {:x}", handle.0);

このマクロは *mut c_void を返しますが、これは両方のデータ型のサイズが同じであるため、直接 HANDLE に変換できます。これにより、OpenProcess が返す値(対象プロセスへの新しいハンドル)にアクセスできます。

NtDelayExecution() を間接システムコールとして呼び出す

let large = 0x8000000000000000 as u64; // Sleep indefinitely
let large: *mut i64 = std::mem::transmute(&large);
let alertable = false;
let ntstatus = unwinder::indirect_syscall!("NtDelayExecution", false, alertable, large); // returns *mut c_void
println!("ntstatus: {:x}", ntstatus as i32);

このマクロは *mut c_void を返し、NtDelayExecution が返す NTSTATUS を取得するために使用できます。

マクロ呼び出しの連結

スプーフィング処理は、コールスタックサイズが異常に増加することなく、何度でも連結できます。実行フローも維持されます。以下はその例です:

fn main()
{
	function_a();
}

fn function_a()
{
	unsafe
	{
		let func_b = function_b as usize;
		call_function!(func_b, false);
		println!("function_a done.");
	}
}

fn function_b()
{
	unsafe
	{
		let func_c = function_c as usize;
		call_function!(func_c, false);
		println!("function_b done.")
	}
}

fn function_c()
{
	unsafe
	{
		let large = 0x0000000000000000 as u64; // Don't sleep so we return to function_b, allowing to check the execution flow preservation.
		let large: *mut i64 = std::mem::transmute(&large);
		let alertable = false;
		let ntstatus = unwinder::indirect_syscall!("NtDelayExecution", false, alertable, large);
		println!("ntstatus: {:x}", (ntstatus as usize) as i32); //NTSTATUS is a i32, although that second casting is not really required in this case.
	}
}

注意事項

最初のフレーム

2つ目のパラメータを true に設定すると(両マクロとも)、スプーフィング処理はコールスタック内にスレッド開始アドレスのフレームを保持して、正当性を高めようとします。

メインモジュールを保持したスプーフィング済みコールスタック。

スレッドの開始関数が後続の関数への call を実行しない場合(代わりに jmp 命令が実行される場合など)、スタックにリターンアドレスがプッシュされないことになります。そのようなシナリオでは(2つ目のパラメータを false に設定した場合も)、スプーフィングされたコールスタックは BaseThreadInitThunk のフレームから始まります。

メインモジュールなしのスプーフィング済みコールスタック。

PoC

この技術の実装をテストするために、PE-sieve を /threads フラグ付きで使用しました。テスト結果は、このクレートの機能を使用した場合、コールスタックの検査によってペイロードの存在が明らかにならないことを示しています。2枚目の画像でわかるように、unwinder を使用しない場合はペイロードが検出されます。

unwinder を使用した場合の PE-sieve の結果。 unwinder を使用しない場合の PE-sieve の結果。

スタック置換

技術の説明

これは SilentMoonWalk に代わるコールスタックスプーフィング手法で、プログラムの実行中にクリーンなコールスタックを維持できます。この技術の背後にある主なアイデアは、モジュール内で呼び出される各関数が、以前にプッシュされたリターンアドレスを処理し、スプーフィング対象のリターンアドレスと同じフレームサイズを持つ正当な関数を実行時に見つけるというものです。同じフレームサイズを持つ正当な関数が見つかると、その関数内のオフセットが計算され、最終的なアドレスを使用して最後のリターンアドレスを置換し、コールスタック内の異常なエントリを隠して、アンワインド可能な状態を維持します。元のリターンアドレスは unwinder によって保存され、return 命令が実行される前にスタック上の正しい位置に戻されるため、プログラムの通常のフローを継続できます。

スタック置換

これは実験的な機能であり、完全に機能するものの、まだ開発と研究が進行中です。この技術をコードに統合する場合は、必ずテストしてください。

使用方法

スタック置換機能を使用するには、cargo.toml に次の行を追加し、release モードでコンパイルしてください:

[dependencies]
unwinder = {version = "0.1.4", features = ["Experimental"]}

この機能の主な機能は次のマクロにまとめられています:

  • start_stack_replacement!()/end_replacement!() のマクロペアは、unwinder にスタック置換プロセスの開始/終了を指示します。これらの2つのマクロは、コードのエントリポイント(例: DLL のエクスポート関数)で呼び出す必要があります。
  • replace_and_continue!()/restore!() のマクロペアは、最後のリターンアドレスの置換/復元を実行します。
  • 最後に、replace_and_call!()/replace_and_syscall!() のマクロペアは、現在のモジュール外の関数を呼び出すときにスタック置換を実行するために使用されます(例: Windows API を使用する場合、または他の DLL のコードを呼び出す場合)。これらのマクロはどちらも、この方法で呼び出された関数が返す値を含む *mut c_void を返します(つまり、SilentMoonWalk を実行するために使用される call_function および indirect_syscall マクロについて説明したのと同じ方法で動作します)。
ツールをダウンロード