Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
Unwinder — Трассировка стека вызовов для Rust | Kitploit
Инструменты/GitHubGitHub/kudaes/unwinder
Обход IDS/IPSПост-эксплуатацияRed TeamingРазработка Полезной НагрузкиСостязательная Атака
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.
  • dinvoke_rs используется для выполнения любых необходимых крейту вызовов Windows API.

Благодарности

Благодарность создателям техники SilentMoonWalk:

  • KlezVirus
  • Waldo-IRC
  • Trickster0

И, конечно, огромная благодарность namazso за тред в Twitter, который вдохновил весь этот проект.

Использование

Чтобы подключить этот крейт к вашему проекту, добавьте следующую строку в ваш cargo.toml и выполните сборку в режиме release:

[dependencies]
unwinder = "=0.1.4"

Основная функциональность этого крейта обёрнута в два макроса:

  • Макрос call_function!() позволяет выполнять любую произвольную функцию с чистым стеком вызовов.
  • Макрос indirect_syscall!() выполняет указанный (непрямой) системный вызов с чистым стеком вызовов.

Для использования любого из этих макросов необходимо импортировать тип данных std::ffi::c_void.

Оба макроса возвращают *mut c_void, который можно использовать для получения значения, возвращённого выполненной функцией. Более подробная информация приведена в разделе с примерами.

Макрос call_function

Этот макрос используется для вызова любой нужной функции с чистым стеком вызовов. Макрос ожидает следующие параметры:

  • Первый параметр — адрес памяти, по которому выполняется вызов после спуфинга стека вызовов. Этот параметр должен передаваться как usize, isize или указатель.
  • Второй параметр — логическое значение (bool), указывающее, нужно ли сохранять кадр стартовой функции. Если вы не уверены в этом, установите его в false — это всегда гарантирует корректный стек вызовов.
  • Следующие параметры — это аргументы, которые будут переданы функции после спуфинга стека вызовов.

Макрос indirect_syscall

Этот макрос используется для выполнения любого нужного непрямого системного вызова с чистым стеком вызовов. Макрос ожидает следующие параметры:

  • Первый параметр — строка, содержащая имя NT-функции, системный вызов которой вы хотите выполнить.
  • Второй параметр — логическое значение (bool), указывающее, нужно ли сохранять кадр стартовой функции. Если вы не уверены в этом, установите его в false — это всегда гарантирует корректный стек вызовов.
  • Следующие параметры — это аргументы, которые будут переданы NT-функции.

Передача параметров

Чтобы передавать этим двум макросам аргументы различных типов, необходимо учитывать следующее:

  • Любой базовый тип данных, который может быть преобразован в usize (u8-u64, i8-i64, bool и т.д.), может передаваться макросам напрямую.
  • Структуры и объединения размером 8, 16, 32 или 64 бита передаются так, как если бы они были целыми числами того же размера.
  • Структуры и объединения размером более 64 бит должны передаваться как указатель.
  • Строки (&str и String) должны передаваться как указатель.
  • Нулевые указатели (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, который можно использовать для получения NTSTATUS, возвращённого функцией NtDelayExecution.

Объединение вызовов макросов

Процесс спуфинга может быть выстроен в цепочку любое количество раз без аномального увеличения размера стека вызовов. Поток выполнения при этом также сохраняется. Ниже приведён пример:

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.
	}
}

Важные замечания

Начальный кадр

Если установить второй параметр в true (в обоих макросах), процесс спуфинга попытается сохранить в стеке вызовов кадр стартового адреса потока, чтобы повысить легитимность.

Скачать инструмент