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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
NullGate — 間接システムコールの利用を容易にするライブラリ。PoCとしてのAV/EDRバイパスとしてかなり興味深い。 | Kitploit
ツール/GitHubGitHub/0xsch1zo/nullgate
暗号化/復号化ツールシェルコードポストエクスプロイトレッドチーミングペイロード開発敵対的攻撃
GitHub0xsch1zo/nullgate

NullGate

間接システムコールの利用を容易にするライブラリ。PoCとしてのAV/EDRバイパスとしてかなり興味深い。

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

人気

すべて見る →

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

すべてのツールを探索

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

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

NullGate

このプロジェクトは、間接システムコールを使用してNTAPI関数を快適かつ現代的に利用する方法を実装しており、動的なシステムコール番号の取得には FreshyCalls メソッドを少しひねった形で組み合わせています。 また、Windows Defender のメモリスキャンを回避するために、これまで言及されているのを見たことがないテクニックも使用しています。 このプロジェクトは、このライブラリを使用した古典的なPoCプロセスインジェクタを実装しています。 便利な暗号化関数も利用できます。

Demo

Demonstration of the sample

Build

サンプルをビルドするには -DNULLGATE_BUILD_SAMPLE=ON を使用します。 nullgateを直接ビルドした場合は <build_dir>/sample.exe に、依存関係としてビルドした場合は <build_dir>/_deps/nullgate-build/sample.exe にあります。 Windowsではビルド先が奇妙なため、おそらくサンプルの場所と同じベースディレクトリにありますが、さらに何層か深くネストされている可能性があります。 引数として、シェルコードをインジェクトしたいPIDを受け取ります。

[!WARNING] Linuxを使用している場合、mingwクロスコンパイラをインストールする必要があります。Archでは例えば pacman -S mingw-w64-gcc を実行できます。その後、-DNULLGATE_CROSSCOMPILE=ON オプションを使用して、mingwをデフォルトのコンパイラに設定します。

[!TIP] また、検出される可能性を減らすために、生成されたバイナリをストリップすることも推奨されます。

root@kitploit:~
git clone https://github.com/0xsch1zo/NullGate
cd NullGate
cmake . -B build -DNULLGATE_BUILD_SAMPLE=ON
cmake --build build/

非推奨のhashser(バージョン1.1.3以下)

-DNULLGATE_DEPRECATED_HASHER フラグを使用してビルドできます。

Usage

Adding nullgate to your project

CMake FetchContent がサポートされています。以下はシンプルな CMakeLists.txt の例です:

root@kitploit:~
cmake_minimum_required(VERSION 3.25)

include(FetchContent)

FetchContent_Declare(nullgate
    GIT_REPOSITORY https://github.com/0xsch1zo/NullGate
    GIT_TAG 1.2.0 
)

FetchContent_MakeAvailable(nullgate)

project(test)

add_executable(test
    main.cpp
)

target_link_libraries(test
    PRIVATE nullgate
)

リンクは静的に行われるため、シンボルが外部に見えることを心配する必要はありません。

[!NOTE] 以降の例では namespace ng = nullgate を使用します。

Syscalls

使い方は非常に簡単です。主な機能を示すスニペットを次に示します:

root@kitploit:~
ng::syscalls syscalls;
typedef NTSTATUS NTAPI NtAllocateVirtualMemory(
    _In_ HANDLE ProcessHandle,
    _Inout_ _At_(*BaseAddress,
                 _Readable_bytes_(*RegionSize) _Writable_bytes_(*RegionSize)
                     _Post_readable_byte_size_(*RegionSize)) PVOID *BaseAddress,
    _In_ ULONG_PTR ZeroBits, _Inout_ PSIZE_T RegionSize,
    _In_ ULONG AllocationType, _In_ ULONG PageProtection);

NTSTATUS status = syscalls.SCall<NtAllocateVirtualMemory>(
      ng::obfuscation::fnv1Const("NtAllocateVirtualMemory"), processHandle,
      &buf, 0, &regionSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

型安全性が組み込まれています。 呼び出したいNT関数の定義を提供するだけでよいのです!それは ntdoc から簡単に取得できます。 これがライブラリの 推奨 される使い方です。

以前の非推奨ではないインターフェース

C++テンプレートの黒魔術が好きではない方のために、以前のインターフェースも引き続き利用できます:

root@kitploit:~
NTSTATUS status = syscalls.Call(ng::obfuscation::fnv1Const("NtAllocateVirtualMemory"),
                         processHandle, (PVOID)&buf, (ULONG_PTR)0, &regionSize,
                         (ULONG)(MEM_RESERVE | MEM_COMMIT), (ULONG)PAGE_EXECUTE_READWRITE);

このインターフェースを使用する場合は、引数を正しい型にキャストする必要があります。これを行わないと 問題 が発生する可能性があります。

Encryption/hashing

ntapi呼び出しのハッシュ化

root@kitploit:~
constexpr uint64_t hash = ng::obfuscation::fnv1Const("NtAllocateVirtualMemory");

先ほど示した fnv1Const メソッドは、モダンC++の喜びをマルウェア開発(maldev)の世界にもたらします。これは consteval 関数であるため、コンパイル時に評価されることが保証され、読みやすい関数名をfnv1ハッシュに置き換えます。

また、fnv1Runtime というランタイム版もありますが、もちろん関数名を難読化する利点はありません。これは、ntdll内のどの関数からシステムコール番号を取得するかを確認するために実装で使用されます。

一般的なxor暗号化

xor「暗号化」には3つのルーチンがあります:

xorConst
root@kitploit:~
constexpr ng::obfuscation::ConstData xored = ng::obfuscation::xorConst("some string");
std::cout << xored.string();

出力例:

root@kitploit:~
<Y:-E&X0

このルーチンは fnv1Const 関数と同様にコンパイル時に評価されるため、"some string" は暗号化された形式で保存され、バイナリ内に現れることはありません。 しかし、そのデータを実際に使わなければなりません!ConstData は、一定のサイズを持つ生のバイト列の薄いラッパーです。 raw() と string() の2つのメソッドがあります。 raw() は std::vector<unsigned char> を返し、string は名前が示すとおり std::string を返します。

[!NOTE] ConstData を文字列リテラルで直接構築する必要がある場合は、std::to_array を使って中間配列を構築し、それを ConstData に渡してください。 ただし、この方法では ConstData にリテラルの余分なヌル文字も格納されることに注意してください。

もちろん、これを復号化して使用する方法が必要です。次に説明する関数がそれです。

xorRuntime

xorRuntime は利用可能な2番目のルーチンです。 名前が示すとおり、xorConst のランタイム版です。 復号化専用ではなく、暗号化と復号化の両方向に使用できます。ただし、暗号化に使用してもコンパイル時に文字列を難読化する利点はありません。

root@kitploit:~
ng::obfuscation::ConstData xored = ng::obfuscation::xorConst("some string");
ng::obfuscation::ConstData data = ng::obfuscation::xorRuntime(xored);
assert(data.string() == "some string");

std::string dynamicString = "some data of dynamic size";
auto dynamicData = ng::obfuscation::DynamicData(dynamicString);
auto xoredDynamic = ng::obfuscation::xorRuntime(dynamicData);
auto unxoredDynamic = ng::obfuscation::xorRuntime(xoredDynamic);
assert(unxoredDynamic.string() == dynamicString);

DynamicData は ConstData と同じメソッドを持ち、相互運用性のためのコンストラクタがいくつか追加されています。名前が示すとおり、コンパイル時にサイズが不明なデータも扱えます。 xorRuntime は DynamicData と ConstData の両方を受け入れます。

xorRuntimeDecrypted

実行時に毎回 xorConst を呼び出してからその結果を xorRuntime に渡して復号化することもできますが、それは少し面倒です。 そこで登場するのがこの問題の解決策、そして最高の仕上げである xorRuntimeDecrypted です。これは、暗号化された状態で操作する必要のない文字列リテラルのための便利な関数です。 コンパイル時に文字列リテラルを暗号化し、実行時に復号化する処理を1回の呼び出しで行います。すごいと思いませんか!

root@kitploit:~
ng::obfuscation::ConstData text = ng::obfuscation::xorRuntimeDecrypted<"some string">();
assert(data.string() == "some string");
鍵

「すべて素晴らしいが、鍵はどこにあるのか?」と言う人もいるでしょう。NullGate 1.2 では、ビルド(つまり cmake コマンドの実行)のたびに鍵がランダムに生成されます! これにより、シグネチャ検出される可能性をさらに減らすことができます。

Windows Defender メモリスキャン回避

問題の中核は、NtCreateRemoteThreadEx または NtCreateProcess を呼び出すとメモリスキャンがトリガーされ、シグネチャだらけの msfvenom ペイロードが検出されてしまうことです。

それを回避するには?

既知の解決策は、まず NtAllocateVirtualMemory を呼び出す際にページの権限を PAGE_NOACCESS に設定し、その後スレッドをサスペンド状態で作成するというものです。 これにより、Windows Defender がプロセスのメモリをスキャンしようとしても、それができなくなります。 その後、NtResumeThread でスレッドの実行を再開できます。 これは機能しますが、より有能なセキュリティソリューションが使われている場合はどうでしょうか?それは何をするでしょうか? もちろん、VirtualProtect を使ってページの権限を変更し、msfvenom を検出するだけです。 これを回避するために、私は戦略を少し変更しました。ページを PAGE_NOACCESS に設定する代わりに、プロセスのメモリへの最初の書き込み時に、ジャンクデータをプロセスに入れるだけです(はい、これが必要です。あるいは、これなしで動作させる方法を見つけられないだけかもしれません)。 その後、スレッドをサスペンド状態で作成します。 その後、プロセスに目的のシェルコードを書き込み、最後に NtResumeThread を使用してスレッドを再開します。 このテクニックを使えば、NtCreateThreadEx の呼び出し後にメモリにアクセスされることを心配する必要はありません。そこには何もないからです。 実際に復号化されたシェルコードが書き込まれ、実行が再開されるのは、その後のことです。

謝辞:

  • 動的なシステムコール番号の取得という優れた方法と、私が多大なインスピレーションを得たプロジェクト全体について、@ElephantSe4l と @MarioBartolome に感謝します。
  • 直接的および間接的なシステムコールについて解説した素晴らしい ブログ記事 と 動画 について、@cr-0w に感謝します。
  • 初期の回避方法を説明した 記事 について、bordergate に感謝します。

免責事項

このライブラリは学術目的のみで作成されました。著者はこのライブラリに与えられたものについて責任を負いません。したがって、誤用から生じるいかなる責任も負いません。

ツールをダウンロード