annihilatorq/omni
GitHub: annihilatorq/omni
一个现代 C++23 零分配头文件库,封装了 Windows 用户态底层操作,包括 PEB 模块遍历、导出表解析、延迟导入和直接 syscall 调用。
Stars: 254 | Forks: 23
[](https://github.com/annihilatorq/shadow_syscall/actions/workflows/windows-msvc.yml)
[](https://github.com/annihilatorq/shadow_syscall/actions/workflows/windows-clang-cl.yml)
[](https://github.com/annihilatorq/shadow_syscall/actions/workflows/windows-gcc.yml)
[](https://github.com/annihilatorq/shadow_syscall/actions/workflows/windows-msvc-no-exceptions.yml)
# omni
`omni` 为通常较为繁琐的 WinAPI 操作提供了现代 C++ 封装:遍历加载器列表、解析导出表、通过哈希解析导入、调用 syscall 以及读取共享用户数据。它在需要迭代的地方保持了良好的 range 兼容性,简化了常见的调用流程,并提供了类型化的封装作为可选的额外验证。
## 核心特性
- 仅头文件的 CMake target:`omni::omni`
- 通过 `omni::modules`、`omni::module` 和 `omni::module_exports` 进行加载器/模块/导出检查
- 通过简洁的自由函数重载实现延迟导入和 syscall 调用
- 通过 `omni::lazy_importer` 和 `omni::syscaller` 提供可选的类型化封装
- 支持隐式字面量友好构造函数的编译时字符串哈希
- 对模块和导出提供友好的 Ranges 迭代器
- 为 syscall ID 和延迟导入提供可选缓存
- 针对加载器语义、导出解析、缓存和 syscall 解析的 Windows 特定测试
## 快速开始
```
add_subdirectory(path/to/omni)
target_link_libraries(your_target PRIVATE omni::omni)
```
```
#include
#include "omni/modules.hpp"
#include "omni/syscall.hpp"
#include
#include
int main() {
auto kernel32 = omni::get_module(L"kernel32.dll");
auto yield_status = omni::syscall("NtYieldExecution");
std::println("module : {}", kernel32.name());
std::println("exports : {}", kernel32.exports().size());
std::println("yield : 0x{:08X}", yield_status);
}
```
## API 概览
| 区域 | 主要类型 |
| --- | --- |
| Syscall | `omni::syscall`、`omni::syscaller`、`omni::status` |
| 延迟导入 | `omni::lazy_import`、`omni::lazy_importer` |
| 加载器检查 | `omni::modules`、`omni::module`、`omni::module_export`、`omni::module_exports` |
| 共享数据 | `omni::shared_user_data` |
| 工具 | `omni::address`、`omni::allocator`、`omni::fnv1a32`、`omni::fnv1a64`、自定义哈希策略 |
## 示例
[`examples/`](examples) 中的每个文件都会构建为独立的可执行文件:
`address` · `allocator` · `hash` · `lazy_import` · `module` · `module_exports` · `modules` · `shared_user_data` · `status` · `syscall`
```
#include
#include "omni/module.hpp"
#include "omni/modules.hpp"
#include
#include
#include
namespace {
[[nodiscard]] std::string_view name_of_export(const omni::module_export& export_entry) {
return export_entry.name;
}
} // namespace
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);
}
}
```
示例输出:
```
AcquireSRWLockExclusive
AcquireSRWLockShared
ActivateActCtx
AddAtomA
AddAtomW
```
```
#include
#include "omni/lazy_import.hpp"
#include
#include
#include
namespace {
using get_module_handle_w_fn = HMODULE(WINAPI*)(LPCWSTR);
using get_system_directory_w_fn = UINT(WINAPI*)(LPWSTR, UINT);
} // namespace
int main() {
auto get_module_handle = omni::lazy_importer{"GetModuleHandleW"};
HMODULE kernel32_handle = get_module_handle(L"kernel32.dll");
auto get_module_handle_export = get_module_handle.module_export();
if (kernel32_handle == nullptr || !get_module_handle_export.present()) {
std::println("Failed to lazy-import GetModuleHandleW");
return 1;
}
std::println("Typed lazy importer:");
std::println(" result : {:#x}", reinterpret_cast(kernel32_handle));
std::println(" export address : {:#x}", get_module_handle_export.address.value());
std::println(" owning module : {}", omni::get_module(get_module_handle_export.module_base).name());
auto system_directory_buffer = std::array{};
auto system_directory_length = omni::lazy_import({"GetSystemDirectoryW", "kernel32.dll"},
system_directory_buffer.data(),
static_cast(system_directory_buffer.size()));
auto system_directory = std::filesystem::path{
std::wstring_view{system_directory_buffer.data(), system_directory_length},
};
std::println();
std::println("Hash-pair overload:");
std::println(" system directory : {}", system_directory.string());
auto process_id = omni::lazy_import<::GetCurrentProcessId>();
std::println();
std::println("Auto-function overload:");
std::println(" current process id : {}", process_id);
omni::lazy_import("SetLastError", 0xCAFEU);
auto last_error = ::GetLastError();
std::println();
std::println("Generic return-type overload:");
std::println(" GetLastError() : 0x{:X}", last_error);
auto missing_export = omni::lazy_importer{"MissingExportForExamples", "kernel32.dll"}.try_invoke();
if (!missing_export) {
std::println();
std::println("Failure diagnostics stay explicit:");
std::println(" {}", missing_export.error().message());
}
}
```
示例输出:
```
process id : 18432
result : 0x7ff9d3d40000
export address : 0x7ff9d3d5b7f0
```
```
#include
#include "omni/syscall.hpp"
#include
#include
struct process_basic_information {
void* reserved1{};
void* peb_base_address{};
void* reserved2[2]{};
std::uintptr_t unique_process_id{};
void* reserved3{};
};
using nt_query_info_process_fn = omni::status (*)(HANDLE, ULONG, void*, ULONG, ULONG*);
int main() {
omni::syscaller query_process{"NtQueryInformationProcess"};
process_basic_information process_info{};
ULONG return_length{};
auto query_status = query_process.try_invoke(::GetCurrentProcess(), 0U, &process_info, sizeof(process_info), &return_length);
if (!query_status) {
std::println("Failed to resolve NtQueryInformationProcess: {}", query_status.error().message());
return 1;
}
process_basic_information shortcut_process_info{};
ULONG shortcut_return_length{};
auto shortcut_status = omni::syscall("NtQueryInformationProcess",
::GetCurrentProcess(),
0U,
&shortcut_process_info,
sizeof(shortcut_process_info),
&shortcut_return_length);
std::println("Typed syscall wrapper around NtQueryInformationProcess:");
std::println(" status : 0x{:08X}", static_cast(query_status->value));
std::println(" success : {}", query_status->is_success());
std::println(" PEB : {:#x}", reinterpret_cast(process_info.peb_base_address));
std::println(" process id : {}", process_info.unique_process_id);
std::println(" return length : {}", return_length);
std::println();
std::println("Free overload with a typed function signature:");
std::println(" status : 0x{:08X}", static_cast(shortcut_status.value));
std::println(" same PEB : {}", shortcut_process_info.peb_base_address == process_info.peb_base_address);
std::println(" same process id : {}", shortcut_process_info.unique_process_id == process_info.unique_process_id);
std::println(" return length : {}", shortcut_return_length);
auto yield_status = omni::syscall("NtYieldExecution");
std::println();
std::println("Generic syscall overload:");
std::println(" NtYieldExecution : 0x{:08X}", static_cast(yield_status.value));
std::println(" success : {}", yield_status.is_success());
auto not_a_syscall = omni::syscaller{"RtlGetVersion"}.try_invoke();
if (!not_a_syscall) {
std::println();
std::println("Diagnostics stay explicit when an export is not a syscall stub:");
std::println(" {}", not_a_syscall.error().message());
}
}
```
示例输出:
```
status : 0x00000000
success : true
typed : 0x00000000
```
更多完整示例,请参见:
- [`examples/hash.cpp`](examples/hash.cpp)
- [`examples/module_exports.cpp`](examples/module_exports.cpp)
- [`examples/modules.cpp`](examples/modules.cpp)
- [`examples/shared_user_data.cpp`](examples/shared_user_data.cpp)
## 构建
要求:
- Windows
- CMake 3.21+
- C++23 编译器
构建库、所有示例和测试套件:
```
cmake -S . -B build -DOMNI_BUILD_EXAMPLES=ON -DOMNI_BUILD_TESTS=ON
cmake --build build --config Release
```
常用 CMake 选项:
| 选项 | 默认值 | 含义 |
| --- | --- | --- |
| `OMNI_BUILD_EXAMPLES` | 顶层构建默认为 `ON` | 将 `examples/` 中的每个文件构建为独立的可执行文件 |
| `OMNI_BUILD_TESTS` | 顶层构建默认为 `ON` | 构建 Windows 测试套件 |
| `OMNI_DISABLE_EXCEPTIONS` | `OFF` | 禁用 C++ 异常 |
## 测试
测试套件位于 [`tests/`](tests),每个测试源文件会构建一个名为 `omni_test_` 的可执行文件,并注册到 `CTest`。
当前涵盖的测试范围包括:
- 加载器迭代与查找
- `module` 标识及面向 WinAPI 的属性
- 导出表解析、按名称/序号迭代以及转发器
- 延迟导入的成功/失败路径、类型化重载及缓存
- syscall 解析、类型化/泛型封装及缓存行为
使用 CTest 运行测试套件:
```
ctest --test-dir build --output-on-failure
```
如果你使用多配置生成器,请附加 `-C Release` 或你所选的配置。
测试使用了 [`boost.ut`](https://github.com/boost-ext/ut)。如果本地不可用,CMake 会自动拉取它。
## 配置
`omni` 基本上是零配置的,但也提供了一些编译时开关:
| 宏 | 效果 |
| --- | --- |
| `OMNI_DISABLE_CACHING` | 禁用延迟导入和 syscall ID 的内部缓存 |
| `OMNI_ENABLE_ERROR_STRINGS` | 在非 Debug 构建中保留可读的错误字符串 |
| `OMNI_DISABLE_ERROR_STRINGS` | 即使在 Debug 构建中也剥离错误字符串 |
## 备注
- `syscall` 示例假定运行在 x64 Windows 进程中。
- 函数的哈希参数确保在编译时将你传入的字符串字面量转换为数字。
- 该项目仍在不断发展中,因此在库的接口稳定之前,预计还会有 API 调整。
- 第三方声明详见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。
感谢 `receiver1`、`po0p` 和 invers1on 为本项目提供的贡献、想法和帮助。
examples/module.cpp — 像普通 range 一样迭代导出项
```
#include examples/lazy_import.cpp — 简单的调用优先,需要元数据时使用类型化访问
```
#include examples/syscall.cpp — 原生 syscall 优先,需要额外检查时使用类型化封装
```
#include 标签:Bash脚本, C++23, CMake, C++封装库, DNS 解析, EDR绕过, Hpfeeds, PEB遍历, Ranges, Raspberry Pi, shadow_syscall, Shellcode辅助, Syscall, Web开发, Windows API, Windows开发, XML 请求, 中高交互蜜罐, 云资产清单, 内存执行, 动态解析, 头文件库, 安全工具库, 安全开发, 安全意识培训, 导出表遍历, 延迟导入, 恶意软件开发, 模块解析, 用户态, 端点可见性, 系统调用, 红队基础设施, 编译期字符串哈希, 被动侦察, 逆向工程, 零内存分配, 高交互蜜罐