C++ Исходник Shadow syscalls (x64, MASM branch, Shellcode branch)

артём диджитал дизайнер
Участник
Статус
Оффлайн
Регистрация
10 Окт 2020
Сообщения
490
Реакции[?]
476
Поинты[?]
65K
Thank you for this library.
Hi mate, no problem, i glad that my little library is helping other coders)

Shellcode branch has undefined behavior where across compilations it sometimes cant find export address to common calls like "NtQuerySystemInformation". MASM branch works.
at this moment i am not seeing any errors with this example

C++:
int main()
{
    SYSTEM_BASIC_INFORMATION basic_info;

    NTSTATUS status = shadowsyscall(NTSTATUS, NtQuerySystemInformation).call(
        SystemBasicInformation,
        &basic_info,
        sizeof(basic_info),
        nullptr);

    std::cout << "Status: 0x" << std::hex << status << '\n';
    std::cout << std::dec << static_cast<int>(basic_info.NumberOfProcessors) << '\n';

    return 0;
}
if you add me on discord and describe the problem in more detail, i'd be happy to help you out
my discord - ntraiseharderror
 
артём диджитал дизайнер
Участник
Статус
Оффлайн
Регистрация
10 Окт 2020
Сообщения
490
Реакции[?]
476
Поинты[?]
65K
Обновочка

shadow syscall shellcode.hpp :
[+] - Удалена зависимость <unordered_map>
[+] - Добавлена реализация простейшего контейнера map (не предназначена для использования вне заголовка)

В целом немного отредачил весь код (нейминги и не только) в лучшую сторону

Репозиторий:
[+] - Обновлён отрывок кода в readme (:astonished:)
 
артём диджитал дизайнер
Участник
Статус
Оффлайн
Регистрация
10 Окт 2020
Сообщения
490
Реакции[?]
476
Поинты[?]
65K
всем привет! завёз очень крупную обнову в которой наконец переписал то мясо что было в репозитории, и заодно подвёз новый функционал

список изменений:

[+] Добавлена поддержка компилятора gcc
[+] Добавлена поддержка компилятора clang
[+] Добавлен метод shadowcall, делает то же что и lazy_import
[+] Добавлен класс c_modules_range который позволяет енумерироваться по всем модулям загруженным в процесс
[+] Добавлен класс c_module для получения информации о заранее известном модуле
[+] Добавлен класс c_exports для получения информации о EAT (а конкретнее енумерации экспортов) загруженного в процесс dll модуля
[+] Добавлена структура hash_t хэширующая строки на этапе компиляции с гарантией consteval
[+] Добавлены структуры linux-pe, за что большое спасибо
Пожалуйста, авторизуйтесь для просмотра ссылки.
репозиторию и его контрибьюторам
[+] Добавлена адекватная поддержка x86 архитектуры. к сожалению, но метод syscall будет недоступен, однако вы всё ещё можете юзать остальные классы и методы этой библиотеки

[+] Изменено: shadowsyscall который раньше был дефайном, сейчас является функцией
[+] Изменено: каждая ошибка что ранее обозначалась как #error стала static_assert
[+] Изменено: static_assert на x86 архитектуре триггерится только при вызове .shadowsyscall функции. остальной функционал библиотеки всё ещё остаётся рабочим
[+] Изменено: недо-мапа из прошлого репозитория заменена на std::unordered_map

[-] Удалено: фарш из дефайнов
[-] Удалено: метод .cached_call, каждый вызов теперь по дефолту является кэшированным. для полного отключения кэширования необходимо установить у дефайна SHADOWSYSCALLS_CACHING значение false

Пожалуйста, авторизуйтесь для просмотра ссылки.

upd: example

main.cpp:
int main()
{
    shadowcall<HMODULE>( "LoadLibraryA", "user32.dll" );

    /// Enumerate every module loaded to current process
    ///
    for ( auto module : shadow::c_modules_range{} )
        std::wcout << module->name.to_wstring() << "\n";

    /// Find exactly known module loaded to current process
    /// "ntdll.dll" doesn't leave string in executable, it
    /// being hashed on compile-time with conclusive guarantee
    ///
    auto module = shadow::c_module{ "ntdll.dll" };

    /// Enumerate EAT of module
    ///
    for ( auto [export_name, export_address] : shadow::c_exports{ module } )
        std::cout << export_name << " : " << export_address << "\n";

    /// Enumerate sections of module
    ///
    for ( auto section : module.image()->get_nt_headers()->sections() )
        std::cout << section.name.to_string() << "\n";

    /// Execute any export at runtime
    ///
    shadowcall<int>( "MessageBoxA", nullptr, "string 1", "string 2", MB_OK );

    /// We need to call actually MessageBoxA, but not just MessageBox,
    /// because the MessageBox is just a line of code defined by
    /// Microsoft, the actual export is MessageBoxA / MessageBoxW.
    // MessageBoxA( nullptr, "string 1", "string 2", MB_OK );

    /// Execute syscall
    ///
    shadowsyscall<NTSTATUS>( "NtTerminateProcess", reinterpret_cast< HANDLE >( -1 ), -6932 );

    /// Find an export address
    ///
    auto export_address = shadow::find_export_address( "NtTerminateProcess" );
    // do something with the export
}
 
Последнее редактирование:
pasting corporation
Read Only
Статус
Оффлайн
Регистрация
4 Дек 2022
Сообщения
716
Реакции[?]
208
Поинты[?]
2K
всем привет! завёз очень крупную обнову в которой наконец переписал то мясо что было в репозитории, и заодно подвёз новый функционал

список изменений:

[+] Добавлена поддержка компилятора gcc
[+] Добавлена поддержка компилятора clang
[+] Добавлен метод shadowcall, делает то же что и lazy_import
[+] Добавлен класс c_modules_range который позволяет енумерироваться по всем модулям загруженным в процесс
[+] Добавлен класс c_module для получения информации о заранее известном модуле
[+] Добавлен класс c_exports для получения информации о EAT (а конкретнее енумерации экспортов) загруженного в процесс dll модуля
[+] Добавлена структура hash_t хэширующая строки на этапе компиляции с гарантией consteval
[+] Добавлены структуры linux-pe, за что большое спасибо
Пожалуйста, авторизуйтесь для просмотра ссылки.
репозиторию и его контрибьюторам
[+] Добавлена адекватная поддержка x86 архитектуры. к сожалению, но метод syscall будет недоступен, однако вы всё ещё можете юзать остальные классы и методы этой библиотеки

[+] Изменено: shadowsyscall который раньше был дефайном, сейчас является функцией
[+] Изменено: каждая ошибка что ранее обозначалась как #error стала static_assert
[+] Изменено: static_assert на x86 архитектуре триггерится только при вызове .shadowsyscall функции. остальной функционал библиотеки всё ещё остаётся рабочим
[+] Изменено: недо-мапа из прошлого репозитория заменена на std::unordered_map

[-] Удалено: фарш из дефайнов
[-] Удалено: метод .cached_call, каждый вызов теперь по дефолту является кэшированным. для полного отключения кэширования необходимо установить у дефайна SHADOWSYSCALLS_CACHING значение false

Пожалуйста, авторизуйтесь для просмотра ссылки.

upd: example

main.cpp:
int main()
{
    shadowcall<HMODULE>( "LoadLibraryA", "user32.dll" );

    /// Enumerate every module loaded to current process
    ///
    for ( auto module : shadow::c_modules_range{} )
        std::wcout << module->name.to_wstring() << "\n";

    /// Find exactly known module loaded to current process
    /// "ntdll.dll" doesn't leave string in executable, it
    /// being hashed on compile-time with conclusive guarantee
    ///
    auto module = shadow::c_module{ "ntdll.dll" };

    /// Enumerate EAT of module
    ///
    for ( auto [export_name, export_address] : shadow::c_exports{ module } )
        std::cout << export_name << " : " << export_address << "\n";

    /// Enumerate sections of module
    ///
    for ( auto section : module.image()->get_nt_headers()->sections() )
        std::cout << section.name.to_string() << "\n";

    /// Execute any export at runtime
    ///
    shadowcall<int>( "MessageBoxA", nullptr, "string 1", "string 2", MB_OK );

    /// We need to call actually MessageBoxA, but not just MessageBox,
    /// because the MessageBox is just a line of code defined by
    /// Microsoft, the actual export is MessageBoxA / MessageBoxW.
    // MessageBoxA( nullptr, "string 1", "string 2", MB_OK );

    /// Execute syscall
    ///
    shadowsyscall<NTSTATUS>( "NtTerminateProcess", reinterpret_cast< HANDLE >( -1 ), -6932 );

    /// Find an export address
    ///
    auto export_address = shadow::find_export_address( "NtTerminateProcess" );
    // do something with the export
}
ебать дизайнеры умные пошли 🙏 🙏 🙏
 
артём диджитал дизайнер
Участник
Статус
Оффлайн
Регистрация
10 Окт 2020
Сообщения
490
Реакции[?]
476
Поинты[?]
65K
подвез довольно важную обнову, changelog:

[+] Исправлена интерпретация NULL в nullptr, теперь, если параметр функции ожидает указатель, NULL и nullptr будут восприниматься одинаково ( спасибо за объяснение Debounce )
[+] Исправлены вызовы forwarded функций через shadowcall (HeapAlloc, etc.)
[+] Добавлена возможность вызова shadowcall с указанием модуля, в котором следует искать экспорт (позволяет сильно ускорить поиск)

new example:
int main()
{
    shadowcall<int>( { "LoadLibraryA", "kernel32.dll" }, "user32.dll" );

    HANDLE process = reinterpret_cast< HANDLE >( -1 );

    auto start_routine = []( void* ) -> DWORD {
        std::cout << "thread started!\n";
        return 0;
    };

    auto status = shadowsyscall<NTSTATUS>(
        "NtCreateThreadEx",
        &process,
        THREAD_ALL_ACCESS,
        NULL,
        process,
        static_cast< LPTHREAD_START_ROUTINE >( start_routine ),
        0,
        FALSE,
        NULL,
        NULL,
        NULL,
        0
    );

    std::clog << "NtCreateThreadEx call status: 0x" << std::hex << status << '\n';

    shadowcall<int>( { "MessageBoxA", "user32.dll" }, nullptr, "1", "2", MB_OK );

    return 0;
}
Для просмотра содержимого вам необходимо авторизоваться.
 
Последнее редактирование:
Эксперт
Статус
Оффлайн
Регистрация
29 Мар 2021
Сообщения
1,517
Реакции[?]
570
Поинты[?]
6K
всем привет! завёз очень крупную обнову в которой наконец переписал то мясо что было в репозитории, и заодно подвёз новый функционал

список изменений:

[+] Добавлена поддержка компилятора gcc
[+] Добавлена поддержка компилятора clang
[+] Добавлен метод shadowcall, делает то же что и lazy_import
[+] Добавлен класс c_modules_range который позволяет енумерироваться по всем модулям загруженным в процесс
[+] Добавлен класс c_module для получения информации о заранее известном модуле
[+] Добавлен класс c_exports для получения информации о EAT (а конкретнее енумерации экспортов) загруженного в процесс dll модуля
[+] Добавлена структура hash_t хэширующая строки на этапе компиляции с гарантией consteval
[+] Добавлены структуры linux-pe, за что большое спасибо
Пожалуйста, авторизуйтесь для просмотра ссылки.
репозиторию и его контрибьюторам
[+] Добавлена адекватная поддержка x86 архитектуры. к сожалению, но метод syscall будет недоступен, однако вы всё ещё можете юзать остальные классы и методы этой библиотеки

[+] Изменено: shadowsyscall который раньше был дефайном, сейчас является функцией
[+] Изменено: каждая ошибка что ранее обозначалась как #error стала static_assert
[+] Изменено: static_assert на x86 архитектуре триггерится только при вызове .shadowsyscall функции. остальной функционал библиотеки всё ещё остаётся рабочим
[+] Изменено: недо-мапа из прошлого репозитория заменена на std::unordered_map

[-] Удалено: фарш из дефайнов
[-] Удалено: метод .cached_call, каждый вызов теперь по дефолту является кэшированным. для полного отключения кэширования необходимо установить у дефайна SHADOWSYSCALLS_CACHING значение false

Пожалуйста, авторизуйтесь для просмотра ссылки.

upd: example

main.cpp:
int main()
{
    shadowcall<HMODULE>( "LoadLibraryA", "user32.dll" );

    /// Enumerate every module loaded to current process
    ///
    for ( auto module : shadow::c_modules_range{} )
        std::wcout << module->name.to_wstring() << "\n";

    /// Find exactly known module loaded to current process
    /// "ntdll.dll" doesn't leave string in executable, it
    /// being hashed on compile-time with conclusive guarantee
    ///
    auto module = shadow::c_module{ "ntdll.dll" };

    /// Enumerate EAT of module
    ///
    for ( auto [export_name, export_address] : shadow::c_exports{ module } )
        std::cout << export_name << " : " << export_address << "\n";

    /// Enumerate sections of module
    ///
    for ( auto section : module.image()->get_nt_headers()->sections() )
        std::cout << section.name.to_string() << "\n";

    /// Execute any export at runtime
    ///
    shadowcall<int>( "MessageBoxA", nullptr, "string 1", "string 2", MB_OK );

    /// We need to call actually MessageBoxA, but not just MessageBox,
    /// because the MessageBox is just a line of code defined by
    /// Microsoft, the actual export is MessageBoxA / MessageBoxW.
    // MessageBoxA( nullptr, "string 1", "string 2", MB_OK );

    /// Execute syscall
    ///
    shadowsyscall<NTSTATUS>( "NtTerminateProcess", reinterpret_cast< HANDLE >( -1 ), -6932 );

    /// Find an export address
    ///
    auto export_address = shadow::find_export_address( "NtTerminateProcess" );
    // do something with the export
}
вместо того чтоб заставлять юзера патчить код своей зависимости, лучше бы #ifndef SHADOW_SYSCALL_DISABLE_CACHING сделал..
 
Начинающий
Статус
Оффлайн
Регистрация
17 Авг 2024
Сообщения
11
Реакции[?]
0
Поинты[?]
0
Достаточно удобный в использовании враппер сискола.
мой первый (но не последний) гитхаб репо:
Пожалуйста, авторизуйтесь для просмотра ссылки.

в будущем будет обновляться.

буду рад адекватному фидбэку и советам.
а зочем их прятать?
 
артём диджитал дизайнер
Участник
Статус
Оффлайн
Регистрация
10 Окт 2020
Сообщения
490
Реакции[?]
476
Поинты[?]
65K
v1.2 detailed executors example:
// If “set_custom_ssn_parser” was called, the handling
// of the syscall index falls entirely on the user.
//
// This function is gonna be called once if caching is enabled.
// If not, function will be called on every syscall
std::optional<uint32_t> custom_ssn_parser( shadow::syscaller<NTSTATUS>& instance, shadow::address_t export_address ) {
    if ( !export_address ) {
        instance.set_last_error( shadow::errc::ssn_not_found );
        return std::nullopt;
    }
    return *export_address.ptr<std::uint32_t>( 4 );
}

// Pass the function name as a string, it will be converted
// into a number at the compile-time by the hash64_t ctor
void execute_syscall_with_custom_ssn( shadow::hash64_t function_name ) {
    shadow::syscaller<NTSTATUS> sc{ function_name };
    sc.set_custom_ssn_parser( custom_ssn_parser );

    auto current_process = reinterpret_cast<void*>( -1 );
    std::uint32_t debug_port{ 0 };
    auto [status, err] = sc( current_process, 7, &debug_port, sizeof( uint64_t ), nullptr );
    if ( err )
        std::cerr << "Syscall error occured: " << *err << '\n';

    std::cout << "NtQueryInformationProcess status: " << status << ", debug port is: " << debug_port << "\n";
}

int main() {
    execute_syscall_with_custom_ssn( "NtQueryInformationProcess" );

    // Return type may not be specified since v1.2
    shadowcall( "LoadLibraryA", "user32.dll" );

    // When we know where to look for a specified
    // export it is better to specify it right away,
    // it will speed up the search.
    shadowcall( { "MessageBoxA", "user32.dll" }, nullptr, "string 1", "string 2", MB_OK );

    // Execute any export at runtime. Since we have ct constructor -
    // every string will be converted to uint64_t during compilation time
    auto message_box = shadowcall<int>( "MessageBoxA", nullptr, "string 3", "string 4", MB_OK );

    // "message_box" variable is treated same as "int"
    auto function_result = message_box;
    std::wcout << "Result: " << function_result << ", DLL that contains MessageBoxA is: " << message_box.export_location().filepath() << '\n';

    auto process = reinterpret_cast<HANDLE>( -1 );
    const auto current_process = reinterpret_cast<HANDLE>( -1 );
    auto start_routine = []( void* ) -> DWORD {
        std::cout << "\nthread started!\n";
        return 0;
    };

    // 1 variant - handle error by return value
    // Return type may not be specified since v1.2
    auto [status, error] = shadowsyscall( "NtCreateThreadEx", &process, THREAD_ALL_ACCESS, NULL, current_process,
                                          static_cast<LPTHREAD_START_ROUTINE>( start_routine ), 0, FALSE, NULL, NULL, NULL, 0 );

    if ( error )
        std::cout << "NtCreateThreadEx error occured: " << *error << "\n";
    else
        std::cout << "NtCreateThreadEx call status: 0x" << std::hex << status << '\n';

    // 2 variant - when error handling is not required, get a plain return value
    auto simple_status = shadowsyscall( "NtTerminateProcess", reinterpret_cast<HANDLE>( -1 ), -6932 );

    return 0;
}

v1.2 detailed module & shared-data parser example:
int main() {
    // Enumerate every dll loaded to current process
    for ( const auto& dll : shadow::dlls() )
        std::wcout << dll.filepath() << " : " << dll.native_handle() << "\n";

    std::cout.put( '\n' );

    // Find exactly known dll loaded to current process
    // "ntdll.dll" doesn't leave string in executable, it
    // being hashed on compile-time with consteval guarantee
    // The implementation doesn't care about the “.dll” suffix.
    auto ntdll = shadow::dll( "ntdll" /* after compilation it will become 384989384324938 */ );

    std::wcout << "Current .exe filepath: " << shadow::current_module().filepath() << "\n\n"; // Contains same methods as "ntdll"

    std::cout << ntdll.base_address().ptr() << '\n';                         // .base_address() returns address_t
    std::cout << ntdll.native_handle() << '\n';                              // .native_handle() returns void*
    std::cout << ntdll.entry_point() << '\n';                                // .entry_point() returns address_t, if presented
    std::wcout << ntdll.name() << '\n';                                      // .name() returns std::wstrview, "NTDLL.DLL"
    std::wcout << ntdll.filepath() << '\n';                                  // .filepath() returns std::wstrview, "C:\WINDOWS\SYSTEM32\NTDLL.DLL"
    std::cout << ntdll.image()->get_nt_headers()->signature << '\n';         // returns uint32_t, NT magic value
    std::cout << ntdll.image()->get_optional_header()->size_image << "\n\n"; // returns uint32_t, loaded NTDLL image size

    std::cout << "5 exports of ntdll.dll:\n";
    for ( const auto& [name, address] : ntdll.exports() | std::views::take( 5 ) )
        std::cout << name << " : " << address.raw() << '\n';

    std::cout.put( '\n' );

    auto it = ntdll.exports().find_if( []( auto export_data ) -> bool {
        const auto& [name, address] = export_data;
        constexpr auto compiletime_hash = shadow::hash64_t{ "NtQuerySystemInformation" }; // after compilation it will become 384989384324938
        const auto runtime_hash = shadow::hash64_t{}( name );                             // accepts any range that have access by index
        return compiletime_hash == runtime_hash;
    } );

    const auto& [name, address] = *it;
    std::cout << "Found target export:\n" << name << " : " << address << "\n\n";

    // "location" returns a DLL struct that contains this export
    std::wcout << "DLL that contains Sleep export is: " << shadow::dll_export( "Sleep" ).location().name() << "\n\n";

    // shared_data parses KUSER_SHARED_DATA
    // The class is a high-level wrapper for parsing,
    // which will save you from direct work with raw addresses

    auto shared = shadow::shared_data();

    std::cout << shared.safe_boot_enabled() << '\n';
    std::cout << shared.boot_id() << '\n';
    std::cout << shared.physical_pages_num() << '\n';
    std::cout << shared.kernel_debugger_present() << '\n';
    std::wcout << shared.system_root() << '\n';

    std::cout << shared.system().is_windows_11() << '\n';
    std::cout << shared.system().is_windows_10() << '\n';
    std::cout << shared.system().is_windows_7() << '\n';
    std::cout << shared.system().build_number() << '\n';
    std::cout << shared.system().formatted() << '\n';

    std::cout << shared.unix_epoch_timestamp().utc().time_since_epoch() << '\n';
    std::cout << shared.unix_epoch_timestamp().utc().format_iso8601() << '\n';
    std::cout << shared.unix_epoch_timestamp().local().time_since_epoch() << '\n';
    std::cout << shared.unix_epoch_timestamp().local().format_iso8601() << '\n';
    std::cout << shared.timezone_offset<std::chrono::seconds>() << "\n\n";

    // Iterators are compatible with the ranges library
    static_assert( std::bidirectional_iterator<shadow::detail::export_enumerator::iterator> );
    static_assert( std::bidirectional_iterator<shadow::detail::module_enumerator::iterator> );

    return 0;
}

v1.2 readme update
Пожалуйста, авторизуйтесь для просмотра ссылки.

v1.2 commit

external changes
  • shadowsyscall & shadowcall return type may not be specified since v1.2
  • Added support for overriding the SSN parser for shadow::syscaller<>. More details can be found in the new example.
  • Added support for std::ranges for iterators
  • Added KUSER_SHARED_DATA parser implementation

namespace shadow
  • Added a handy wrapper above the address, shadow::address_t, allows you to briefly and clearly cast any address in the code.
  • The syscaller class has been moved from shadow::syscalls to shadow
  • The importer class has been moved from shadow::syscalls to shadow
  • Namespace syscalls deleted
  • Implementations of DLL enumerators and exports moved to namespace detail
  • Added followed functions:
-- dll( hash64_t name ) - returns characteristics of the specified DLL if it is loaded into the process.
-- current_module() - returns the characteristics of the current .exe file which is the host process.
-- dlls() - returns module enumerator, range-based.
-- dll_exports( hash64_t module_name ) - returns export enumerator, range-based & iterator-based.
-- shared_data() - returns shared_data parser object.

namespace shadow::detail:

class basic_hash
  • Renamed and redesigned the hash_t class, instead it is now the basic_hash class, whose implementation is now independent of the passed integral type.
  • The constructor of the basic_hash class is clearly intended only for compile-time strings, the operator() is overloaded to convert strings known only in runtime, same as std::hash
  • The hash generation algorithm was changed to FNV1-a due to overhead of the previous algorithm
  • The overridden operator() accepts any array that has bracket access and has a .size() function

class export_enumerator
- The Iterator is now std::range compatible, and falls under the conditions of the std::bidirectional_iterator group

class module_enumerator
  • The Iterator is now std::range compatible, and falls under the conditions of the std::bidirectional_iterator group
  • Added method .find_if()

class shared_data
- New class added in v1.2. Description:
shared_data parses kernel_user_shared_data filled by the operating system when the process starts. The structure contains a lot of useful information about the operating system. The class is a high-level wrapper for parsing, which will save you from direct work with raw addresses and can greatly simplify your coding process.

- The following methods have been added:
--.raw(), - Raw pointer to KUSER_SHARED_DATA struct
--.kernel_debugger_present() ,
--.safe_boot_enabled(),
--.boot_id(),
--.physical_pages_num(),
--.system_root(),
--.timezone_id(),
--.timezone_offset(),
--.system(), - returns operation_system that is listed below
--.windows_epoch_timestamp(),
--.unix_epoch_timestamp() - returns zoned_time that is listed below

class zoned_time
- New class added in v1.2.
--.utc(), - returns time_formatter in UTC timezone
--.local(), - returns time_formatter in local system timezone

class time_formatter
- New class added in v1.2.
--.format_european(), - "dd.mm.yyyy hh:mm"
--.format_american(), - "mm/dd/yyyy hh:mm"
--.format_iso8601(), - "yyyy-mm-ddThh:mm:ss"
--.time_since_epoch(), - Raw unix timestamp as integral

class operation_system
- New class added in v1.2.
--.safe_boot_enabled(),
--.is_windows_11(),
--.is_windows_10(),
--.is_windows_8_1(),
--.is_windows_8(),
--.is_windows_7(),
--.is_windows_xp(),
--.is_windows_vista(),
--.major_version(),
--.minor_version(),
--.build_number(),
--.formatted() - returns "Windows {}.{} (Build {})" via std::format

class dynamic_link_library
- The following methods have been added:
--.raw(), - Raw pointer to Win32 struct
--.base_address(), - Base address of current DLL
--.native_handle(), - Pointer on base address of current
--.entry_point(), - Address of entrypoint
--.name(), - Name of current DLL as std::wstring_view
--.filepath(), - Filepath to current DLL as std::wstring_view
--.exports() - Exports range-enumerator of current DLL
 
Эксперт
Статус
Оффлайн
Регистрация
29 Мар 2021
Сообщения
1,517
Реакции[?]
570
Поинты[?]
6K
v1.2 detailed executors example:
// If “set_custom_ssn_parser” was called, the handling
// of the syscall index falls entirely on the user.
//
// This function is gonna be called once if caching is enabled.
// If not, function will be called on every syscall
std::optional<uint32_t> custom_ssn_parser( shadow::syscaller<NTSTATUS>& instance, shadow::address_t export_address ) {
    if ( !export_address ) {
        instance.set_last_error( shadow::errc::ssn_not_found );
        return std::nullopt;
    }
    return *export_address.ptr<std::uint32_t>( 4 );
}

// Pass the function name as a string, it will be converted
// into a number at the compile-time by the hash64_t ctor
void execute_syscall_with_custom_ssn( shadow::hash64_t function_name ) {
    shadow::syscaller<NTSTATUS> sc{ function_name };
    sc.set_custom_ssn_parser( custom_ssn_parser );

    auto current_process = reinterpret_cast<void*>( -1 );
    std::uint32_t debug_port{ 0 };
    auto [status, err] = sc( current_process, 7, &debug_port, sizeof( uint64_t ), nullptr );
    if ( err )
        std::cerr << "Syscall error occured: " << *err << '\n';

    std::cout << "NtQueryInformationProcess status: " << status << ", debug port is: " << debug_port << "\n";
}

int main() {
    execute_syscall_with_custom_ssn( "NtQueryInformationProcess" );

    // Return type may not be specified since v1.2
    shadowcall( "LoadLibraryA", "user32.dll" );

    // When we know where to look for a specified
    // export it is better to specify it right away,
    // it will speed up the search.
    shadowcall( { "MessageBoxA", "user32.dll" }, nullptr, "string 1", "string 2", MB_OK );

    // Execute any export at runtime. Since we have ct constructor -
    // every string will be converted to uint64_t during compilation time
    auto message_box = shadowcall<int>( "MessageBoxA", nullptr, "string 3", "string 4", MB_OK );

    // "message_box" variable is treated same as "int"
    auto function_result = message_box;
    std::wcout << "Result: " << function_result << ", DLL that contains MessageBoxA is: " << message_box.export_location().filepath() << '\n';

    auto process = reinterpret_cast<HANDLE>( -1 );
    const auto current_process = reinterpret_cast<HANDLE>( -1 );
    auto start_routine = []( void* ) -> DWORD {
        std::cout << "\nthread started!\n";
        return 0;
    };

    // 1 variant - handle error by return value
    // Return type may not be specified since v1.2
    auto [status, error] = shadowsyscall( "NtCreateThreadEx", &process, THREAD_ALL_ACCESS, NULL, current_process,
                                          static_cast<LPTHREAD_START_ROUTINE>( start_routine ), 0, FALSE, NULL, NULL, NULL, 0 );

    if ( error )
        std::cout << "NtCreateThreadEx error occured: " << *error << "\n";
    else
        std::cout << "NtCreateThreadEx call status: 0x" << std::hex << status << '\n';

    // 2 variant - when error handling is not required, get a plain return value
    auto simple_status = shadowsyscall( "NtTerminateProcess", reinterpret_cast<HANDLE>( -1 ), -6932 );

    return 0;
}

v1.2 detailed module & shared-data parser example:
int main() {
    // Enumerate every dll loaded to current process
    for ( const auto& dll : shadow::dlls() )
        std::wcout << dll.filepath() << " : " << dll.native_handle() << "\n";

    std::cout.put( '\n' );

    // Find exactly known dll loaded to current process
    // "ntdll.dll" doesn't leave string in executable, it
    // being hashed on compile-time with consteval guarantee
    // The implementation doesn't care about the “.dll” suffix.
    auto ntdll = shadow::dll( "ntdll" /* after compilation it will become 384989384324938 */ );

    std::wcout << "Current .exe filepath: " << shadow::current_module().filepath() << "\n\n"; // Contains same methods as "ntdll"

    std::cout << ntdll.base_address().ptr() << '\n';                         // .base_address() returns address_t
    std::cout << ntdll.native_handle() << '\n';                              // .native_handle() returns void*
    std::cout << ntdll.entry_point() << '\n';                                // .entry_point() returns address_t, if presented
    std::wcout << ntdll.name() << '\n';                                      // .name() returns std::wstrview, "NTDLL.DLL"
    std::wcout << ntdll.filepath() << '\n';                                  // .filepath() returns std::wstrview, "C:\WINDOWS\SYSTEM32\NTDLL.DLL"
    std::cout << ntdll.image()->get_nt_headers()->signature << '\n';         // returns uint32_t, NT magic value
    std::cout << ntdll.image()->get_optional_header()->size_image << "\n\n"; // returns uint32_t, loaded NTDLL image size

    std::cout << "5 exports of ntdll.dll:\n";
    for ( const auto& [name, address] : ntdll.exports() | std::views::take( 5 ) )
        std::cout << name << " : " << address.raw() << '\n';

    std::cout.put( '\n' );

    auto it = ntdll.exports().find_if( []( auto export_data ) -> bool {
        const auto& [name, address] = export_data;
        constexpr auto compiletime_hash = shadow::hash64_t{ "NtQuerySystemInformation" }; // after compilation it will become 384989384324938
        const auto runtime_hash = shadow::hash64_t{}( name );                             // accepts any range that have access by index
        return compiletime_hash == runtime_hash;
    } );

    const auto& [name, address] = *it;
    std::cout << "Found target export:\n" << name << " : " << address << "\n\n";

    // "location" returns a DLL struct that contains this export
    std::wcout << "DLL that contains Sleep export is: " << shadow::dll_export( "Sleep" ).location().name() << "\n\n";

    // shared_data parses KUSER_SHARED_DATA
    // The class is a high-level wrapper for parsing,
    // which will save you from direct work with raw addresses

    auto shared = shadow::shared_data();

    std::cout << shared.safe_boot_enabled() << '\n';
    std::cout << shared.boot_id() << '\n';
    std::cout << shared.physical_pages_num() << '\n';
    std::cout << shared.kernel_debugger_present() << '\n';
    std::wcout << shared.system_root() << '\n';

    std::cout << shared.system().is_windows_11() << '\n';
    std::cout << shared.system().is_windows_10() << '\n';
    std::cout << shared.system().is_windows_7() << '\n';
    std::cout << shared.system().build_number() << '\n';
    std::cout << shared.system().formatted() << '\n';

    std::cout << shared.unix_epoch_timestamp().utc().time_since_epoch() << '\n';
    std::cout << shared.unix_epoch_timestamp().utc().format_iso8601() << '\n';
    std::cout << shared.unix_epoch_timestamp().local().time_since_epoch() << '\n';
    std::cout << shared.unix_epoch_timestamp().local().format_iso8601() << '\n';
    std::cout << shared.timezone_offset<std::chrono::seconds>() << "\n\n";

    // Iterators are compatible with the ranges library
    static_assert( std::bidirectional_iterator<shadow::detail::export_enumerator::iterator> );
    static_assert( std::bidirectional_iterator<shadow::detail::module_enumerator::iterator> );

    return 0;
}

v1.2 readme update
Пожалуйста, авторизуйтесь для просмотра ссылки.

v1.2 commit

external changes
  • shadowsyscall & shadowcall return type may not be specified since v1.2
  • Added support for overriding the SSN parser for shadow::syscaller<>. More details can be found in the new example.
  • Added support for std::ranges for iterators
  • Added KUSER_SHARED_DATA parser implementation

namespace shadow
  • Added a handy wrapper above the address, shadow::address_t, allows you to briefly and clearly cast any address in the code.
  • The syscaller class has been moved from shadow::syscalls to shadow
  • The importer class has been moved from shadow::syscalls to shadow
  • Namespace syscalls deleted
  • Implementations of DLL enumerators and exports moved to namespace detail
  • Added followed functions:
-- dll( hash64_t name ) - returns characteristics of the specified DLL if it is loaded into the process.
-- current_module() - returns the characteristics of the current .exe file which is the host process.
-- dlls() - returns module enumerator, range-based.
-- dll_exports( hash64_t module_name ) - returns export enumerator, range-based & iterator-based.
-- shared_data() - returns shared_data parser object.

namespace shadow::detail:

class basic_hash
  • Renamed and redesigned the hash_t class, instead it is now the basic_hash class, whose implementation is now independent of the passed integral type.
  • The constructor of the basic_hash class is clearly intended only for compile-time strings, the operator() is overloaded to convert strings known only in runtime, same as std::hash
  • The hash generation algorithm was changed to FNV1-a due to overhead of the previous algorithm
  • The overridden operator() accepts any array that has bracket access and has a .size() function

class export_enumerator
- The Iterator is now std::range compatible, and falls under the conditions of the std::bidirectional_iterator group

class module_enumerator
  • The Iterator is now std::range compatible, and falls under the conditions of the std::bidirectional_iterator group
  • Added method .find_if()

class shared_data
- New class added in v1.2. Description:
shared_data parses kernel_user_shared_data filled by the operating system when the process starts. The structure contains a lot of useful information about the operating system. The class is a high-level wrapper for parsing, which will save you from direct work with raw addresses and can greatly simplify your coding process.

- The following methods have been added:
--.raw(), - Raw pointer to KUSER_SHARED_DATA struct
--.kernel_debugger_present() ,
--.safe_boot_enabled(),
--.boot_id(),
--.physical_pages_num(),
--.system_root(),
--.timezone_id(),
--.timezone_offset(),
--.system(), - returns operation_system that is listed below
--.windows_epoch_timestamp(),
--.unix_epoch_timestamp() - returns zoned_time that is listed below

class zoned_time
- New class added in v1.2.
--.utc(), - returns time_formatter in UTC timezone
--.local(), - returns time_formatter in local system timezone

class time_formatter
- New class added in v1.2.
--.format_european(), - "dd.mm.yyyy hh:mm"
--.format_american(), - "mm/dd/yyyy hh:mm"
--.format_iso8601(), - "yyyy-mm-ddThh:mm:ss"
--.time_since_epoch(), - Raw unix timestamp as integral

class operation_system
- New class added in v1.2.
--.safe_boot_enabled(),
--.is_windows_11(),
--.is_windows_10(),
--.is_windows_8_1(),
--.is_windows_8(),
--.is_windows_7(),
--.is_windows_xp(),
--.is_windows_vista(),
--.major_version(),
--.minor_version(),
--.build_number(),
--.formatted() - returns "Windows {}.{} (Build {})" via std::format

class dynamic_link_library
- The following methods have been added:
--.raw(), - Raw pointer to Win32 struct
--.base_address(), - Base address of current DLL
--.native_handle(), - Pointer on base address of current
--.entry_point(), - Address of entrypoint
--.name(), - Name of current DLL as std::wstring_view
--.filepath(), - Filepath to current DLL as std::wstring_view
--.exports() - Exports range-enumerator of current DLL
побольше бы requires на темплейты, и возвращать бы обычные строки заместо широких (мы ж все под utf-8 компилим)..

а так плюс реп
 
Сверху Снизу