- Статус
- Оффлайн
- Регистрация
- 10 Окт 2020
- Сообщения
- 535
- Реакции
- 521
Пожалуйста, авторизуйтесь для просмотра ссылки.
Всем привет, возможно некоторые знают старую версию этого репозиторием под именем shadow_syscall. Так как функциональность репозитория уже давно вышла за пределы одних только сисколлов,
Пожалуйста, авторизуйтесь для просмотра ссылки.
что будет логичнее дать ей более общее название, и заодно переписать всю библиотеку полностью, изменив и формализировав API.Всё было имплементировано в
Пожалуйста, авторизуйтесь для просмотра ссылки.
, после опыта написания пары библиотек считаю что спроектировал новый API не идеально, но в разы лучше предыдущего.Новое C++23 API для экспортов:
Omni module:
#include <Windows.h>
#include "omni/module.hpp"
#include "omni/modules.hpp"
#include <print>
#include <ranges>
#include <string_view>
[[nodiscard]] std::string_view name_of_export(const omni::module_export& export_entry) {
return export_entry.name;
}
int main() {
auto self = omni::base_module();
auto kernel32 = omni::get_module(L"kernel32.dll");
auto* optional_header = self.image()->get_optional_header();
std::println("Current image:");
std::println(" name : {}", self.name());
std::println(" path : {}", self.system_path().string());
std::println(" base : {:#x}", self.base_address().value());
std::println(" entry point : {:#x}", self.entry_point().value());
std::println(" size of image : {}", optional_header->size_image);
std::println(" export count : {}", self.exports().size());
std::println();
std::println("Kernel32 convenience helpers:");
std::println(" name : {}", kernel32.name());
std::println(" path : {}", kernel32.system_path().string());
std::println(R"( matches "kernel32" : {})", kernel32.matches_name_hash(L"kernel32"));
std::println(R"( matches "KERNEL32.DLL": {})", kernel32.matches_name_hash(L"KERNEL32.DLL"));
std::println();
std::println("First five named exports from kernel32:");
auto kernel32_exports = kernel32.exports();
auto first_named_exports = kernel32_exports.named() | std::views::transform(name_of_export) | std::views::take(5);
for (std::string_view export_name : first_named_exports) {
std::println(" {}", export_name);
}
}
Главные функциональные изменения:
1. Добавлена поддержка кастомных compile-time хэшей, они обязаны соответствовать описанному концепту в библиотеке.
Пожалуйста, авторизуйтесь для просмотра ссылки.
можно найти в репозитории.2. Имплементирована логика резольвинга ApiSetMap при поиске экспорта. Теперь, если forwarded-экспорт указывает на DLL являющуюся ApiSetом, библиотека автоматически попытается найти хост закрепленный за ApiSetом. Если хост закрепленный за ApiSetом загружен в текущий процесс, то библиотека найдёт его в списке модулей и попытается найти указанную функцию в EAT этого самого модуля.
3. Добавлен
omni::status с методами для упрощения работы с NTSTATUS. В структуре лежит POD std::int32_t из-за чего ABI будет воспринимать этот тип как INTEGER, не передавая hidden-function-pointer в RCX. По этой причине тип можно безопасно использовать для возвращаемых значений Nt/Zw функций4. Добавил несколько видов енумерации экспортов для максимально подходящего вам варианта: .all(), .named(), .ordinal(). Итерация по всем (.all()) экспортам зачастую будет самой долгой из-за устройства EAT и сортировки имён в нём, из-за чего при итерации по num_functions код вынужден произвести O(n) поиск имени в списке num_names.
5. Улучшено удобство класса
omni::address, убран суффикс _tФиксы:
1. Пофикшена неверная итерация по ordinal-экспортам. В shadow_syscall использовалось num_names вместо num_functions)
2. Пофикшена проблема с кэшированием экспорта в
omni::lazy_importer по одному имени, теперь кэшируется module_name_hash и export_name_hash. В shadow_syscall экспорт кэшировался по хэшу имени функции, из-за чего появлялись проблемы если в нескольких загруженных модулях присутствовал экспорт с одинаковым именем.3. Пофикшена проблема с кэшированием экспорта в omni::lazy_importer без проверки на то, был ли перезагружен модуль в процесс. В shadow_syscall игнорировалась ситуация когда DLL модуль внутри которого находился нужный экспорт был выгружен из процесса и загружен снова уже на другом base_address. Теперь это учитывается.
В целом есть ещё очень много минорных фиксов о которых писать нет смысла, библиотека в целом стала гораздо более надежной.
Концептуальные изменения:
1. Zero-allocation design, при обычном использовании библиотеки не будет использовано никаких выделений памяти. Единственные микро-аллокации которые могут произойти, это аллокации при использовании .to_path() (std::filesystem::path аллоцирует строку) и .string() (для конвертации между std::wstring_view в std::string) методов из win::unicode_string. Это методы-утилиты, внутри себя библиотека их не использует. Если не вызывать эти методы - весь код который вы пишете будет работать как view по уже существующей памяти.
2. Новое предсказуемое и четкое разделение API:
-
omni::modules итерируется по omni::module.-
omni::module_exports итерируется по omni::module_export-
omni::api_sets итерируется по omni::api_set-
omni::api_set_hosts итерируется по omni::api_set_host-
omni::syscaller, функция omni::syscall-
omni::lazy_importer, функция omni::lazy_importТесты:
Библиотека теперь имеет хороший набор юнит-тестов и большую CI матрицу из 16 джобов (на момент 26.04.2026), компилируется и тестируется программа на таких компиляторах как: MSVC, Clang-CL, GCC, на архитектурах x64 & x86, Debug & Release сборках. Библиотека так же может быть собрана без исключений.
Для тех кому трудно или для тех кто не имеет желания работать с CMake есть CI воркфлоу который мёржит все header-файлы библиотеки в один:
Пожалуйста, авторизуйтесь для просмотра ссылки.
Спасибо всем тем кто использует библиотеку и ставит звёзды. Если у вас появятся какие-либо проблемы при её использовании, создавайте Issue на гитхаб. Буду рад каждому контрибьютору.
Последнее редактирование: