{"id":17383403,"url":"https://github.com/annihilatorq/shadow_syscall","last_synced_at":"2025-04-04T21:06:37.643Z","repository":{"id":190257280,"uuid":"682253041","full_name":"annihilatorq/shadow_syscall","owner":"annihilatorq","description":"windows syscalls with a single line and a high level of abstraction. has modern cpp20 wrappers and utilities, range-based DLL and export enumeration, wrapper around KUSER_SHARED_DATA. supported compilers: clang, gcc and msvc","archived":false,"fork":false,"pushed_at":"2025-03-21T19:07:19.000Z","size":202,"stargazers_count":172,"open_issues_count":0,"forks_count":21,"subscribers_count":6,"default_branch":"shellcode","last_synced_at":"2025-03-28T20:06:10.396Z","etag":null,"topics":["analysis","cpp","direct-syscalls","export","getmodulehandle","getprocaddress","hashing","header-only","masm","obfuscation","reverse-engineering","shadow-syscalls","shellcode","syscall","syscalls","win-internals"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/annihilatorq.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-23T19:15:29.000Z","updated_at":"2025-03-24T15:35:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"9b5f33b0-8f7f-410a-a38e-ca58a053de06","html_url":"https://github.com/annihilatorq/shadow_syscall","commit_stats":{"total_commits":35,"total_committers":2,"mean_commits":17.5,"dds":0.02857142857142858,"last_synced_commit":"92e505ee4782fee22cb5148b189d3e1c2f7a6436"},"previous_names":["annihilatorq/shadow_syscall"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/annihilatorq%2Fshadow_syscall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/annihilatorq%2Fshadow_syscall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/annihilatorq%2Fshadow_syscall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/annihilatorq%2Fshadow_syscall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/annihilatorq","download_url":"https://codeload.github.com/annihilatorq/shadow_syscall/tar.gz/refs/heads/shellcode","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247249524,"owners_count":20908212,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["analysis","cpp","direct-syscalls","export","getmodulehandle","getprocaddress","hashing","header-only","masm","obfuscation","reverse-engineering","shadow-syscalls","shellcode","syscall","syscalls","win-internals"],"created_at":"2024-10-16T07:41:31.420Z","updated_at":"2025-04-04T21:06:37.624Z","avatar_url":"https://github.com/annihilatorq.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# shadow syscalls\n\nEasy to use syscall/import executor wrapper. Syscall is based on shellcode. Function names passed in arguments are hashed at compile-time.\nSupports x86 architecture, but on x86 `.shadowsyscall()` is inaccessible.\n\nThe repository provides a convenient high-level wrapper over low-level operations, range-based enumerators for modules and their exports. Includes all `GetModuleHandle`, `GetProcAddress` implementations in a much nicer wrapper without leaving any strings in binary.\nAllows calling undocumented DLL functions. Has a built-in forwarded import resolver (HeapAlloc, etc.)\n\n### Supported platforms\nCLANG, GCC, MSVC. Library requires cpp20.\n\n### Quick example\n```cpp\n// Execute \"NtTerminateProcess\" syscall\nshadowsyscall\u003cNTSTATUS\u003e( \"NtTerminateProcess\", reinterpret_cast\u003c HANDLE \u003e( -1 ), -6932 );\n\n// Since version 1.2, the return type may not be specified\nshadowsyscall( \"NtTerminateProcess\", reinterpret_cast\u003c HANDLE \u003e( -1 ), -6932 );\n\n// Execute any export at runtime\n// Since version 1.2, the return type may not be specified\nshadowcall\u003cint\u003e( \"MessageBoxA\", nullptr, \"string 1\", \"string 2\", MB_OK );\n```\n\n\u003e [!IMPORTANT]\\\n\u003e Make sure you load the dll module that contains the export you want to call. For example - to call MessageBoxA, you need to load \"user32.dll\" into the current process.\n\nShellcode uses allocator based on `NtAllocateVirtualMemory` \u0026 `NtFreeVirtualMemory`\n\n## Detailed executors example (x64)\n```cpp\n#include \u003cWindows.h\u003e\n#include \"shadowsyscall.hpp\"\n\n// If “set_custom_ssn_parser” was called, the handling\n// of the syscall index falls entirely on the user.\n//\n// This function is gonna be called once if caching is enabled.\n// If not, function will be called on every syscall\nstd::optional\u003cuint32_t\u003e custom_ssn_parser( shadow::syscaller\u003cNTSTATUS\u003e\u0026 instance, shadow::address_t export_address ) {\n    if ( !export_address ) {\n        instance.set_last_error( shadow::errc::ssn_not_found );\n        return std::nullopt;\n    }\n    return *export_address.ptr\u003cstd::uint32_t\u003e( 4 );\n}\n\n// Pass the function name as a string, it will be converted\n// into a number at the compile-time by the hash64_t ctor\nvoid execute_syscall_with_custom_ssn( shadow::hash64_t function_name ) {\n    shadow::syscaller\u003cNTSTATUS\u003e sc{ function_name };\n    sc.set_custom_ssn_parser( custom_ssn_parser );\n\n    auto current_process = reinterpret_cast\u003cvoid*\u003e( -1 );\n    std::uintptr_t debug_port{ 0 };\n    auto [status, err] = sc( current_process, 7, \u0026debug_port, sizeof( std::uintptr_t ), nullptr );\n    if ( err )\n        std::cerr \u003c\u003c \"Syscall error occurred: \" \u003c\u003c *err \u003c\u003c '\\n';\n\n    std::cout \u003c\u003c \"NtQueryInformationProcess status: 0x\" \u003c\u003c std::hex \u003c\u003c status \u003c\u003c \", debug port is: \" \u003c\u003c debug_port \u003c\u003c \"\\n\";\n}\n\nint main() {\n    execute_syscall_with_custom_ssn( \"NtQueryInformationProcess\" );\n\n    // Return type may not be specified since v1.2\n    shadowcall( \"LoadLibraryA\", \"user32.dll\" );\n\n    // When we know where to look for a specified\n    // export it is better to specify it right away,\n    // it will speed up the search.\n    shadowcall( { \"MessageBoxA\", \"user32.dll\" }, nullptr, \"string 1\", \"string 2\", MB_OK );\n\n    // Execute any export at runtime. Since we have ct constructor -\n    // every string will be converted to uint64_t during compilation time\n    auto message_box = shadowcall\u003cint\u003e( \"MessageBoxA\", nullptr, \"string 3\", \"string 4\", MB_OK );\n\n    // \"message_box\" variable is treated same as \"int\"\n    auto function_result = message_box;\n    std::cout \u003c\u003c \"Result: \" \u003c\u003c function_result \u003c\u003c \", DLL that contains MessageBoxA is: \" \u003c\u003c message_box.export_location().filepath().string() \u003c\u003c '\\n';\n\n    auto process = reinterpret_cast\u003cHANDLE\u003e( -1 );\n    const auto current_process = reinterpret_cast\u003cHANDLE\u003e( -1 );\n    auto start_routine = []( void* ) -\u003e DWORD {\n        std::cout \u003c\u003c \"\\nthread started!\\n\";\n        return 0;\n    };\n\n    // 1 variant - handle error by return value\n    // Return type may not be specified since v1.2\n    auto [status, error] = shadowsyscall( \"NtCreateThreadEx\", \u0026process, THREAD_ALL_ACCESS, NULL, current_process,\n                                          static_cast\u003cLPTHREAD_START_ROUTINE\u003e( start_routine ), 0, FALSE, NULL, NULL, NULL, 0 );\n\n    if ( error )\n        std::cout \u003c\u003c \"NtCreateThreadEx error occured: \" \u003c\u003c *error \u003c\u003c \"\\n\";\n    else\n        std::cout \u003c\u003c \"NtCreateThreadEx call status: 0x\" \u003c\u003c std::hex \u003c\u003c status \u003c\u003c '\\n';\n\n    // 2 variant - when error handling is not required, get a plain return value\n    auto simple_status = shadowsyscall( \"NtTerminateProcess\", reinterpret_cast\u003cHANDLE\u003e( -1 ), -6932 );\n}\n```\n\n## Detailed module \u0026 shared-data parser example\n```cpp\n#include \u003ciostream\u003e\n#include \u003cstring\u003e\n#include \"shadowsyscall.hpp\"\n\nint main() {\n    // Enumerate every dll loaded to current process\n    for ( const auto\u0026 dll : shadow::dlls() )\n        std::cout \u003c\u003c dll.filepath().string() \u003c\u003c \" : \" \u003c\u003c dll.native_handle() \u003c\u003c \"\\n\";\n\n    std::cout.put( '\\n' );\n\n    // Find exactly known dll loaded to current process\n    // \"ntdll.dll\" doesn't leave string in executable, it\n    // being hashed on compile-time with consteval guarantee\n    // The implementation doesn't care about the \".dll\" suffix.\n    auto ntdll = shadow::dll( \"ntdll\" /* after compilation it will become 384989384324938 */ );\n\n    auto current_module = shadow::current_module();\n    std::cout \u003c\u003c \"Current .exe filepath: \" \u003c\u003c current_module.filepath().string() \u003c\u003c \"\\n\";\n    std::cout \u003c\u003c \"Current .text section checksum: \" \u003c\u003c current_module.section_checksum\u003cstd::size_t\u003e( \".text\" ) \u003c\u003c \"\\n\\n\";\n\n    std::cout \u003c\u003c ntdll.base_address().ptr() \u003c\u003c '\\n';                         // .base_address() returns address_t\n    std::cout \u003c\u003c ntdll.native_handle() \u003c\u003c '\\n';                              // .native_handle() returns void*\n    std::cout \u003c\u003c ntdll.entry_point() \u003c\u003c '\\n';                                // .entry_point() returns address_t, if presented\n    std::cout \u003c\u003c ntdll.name().string() \u003c\u003c '\\n';                              // .name() returns win::unicode_string\n    std::cout \u003c\u003c ntdll.filepath().to_path().extension() \u003c\u003c '\\n';             // .filepath() returns win::unicode_string\n    std::cout \u003c\u003c ntdll.image()-\u003eget_nt_headers()-\u003esignature \u003c\u003c '\\n';         // returns uint32_t, NT magic value\n    std::cout \u003c\u003c ntdll.image()-\u003eget_optional_header()-\u003esize_image \u003c\u003c \"\\n\\n\"; // returns uint32_t, loaded NTDLL image size\n\n    std::cout \u003c\u003c \"5 exports of ntdll.dll:\\n\";\n    for ( const auto\u0026 [name, address] : ntdll.exports() | std::views::take( 5 ) )\n        std::cout \u003c\u003c name \u003c\u003c \" : \" \u003c\u003c address.raw() \u003c\u003c '\\n';\n\n    std::cout.put( '\\n' );\n\n    auto it = ntdll.exports().find_if( []( auto export_data ) -\u003e bool {\n        const auto\u0026 [name, address] = export_data;\n        constexpr auto compiletime_hash = shadow::hash64_t{ \"NtQuerySystemInformation\" }; // after compilation it will become 384989384324938\n        const auto runtime_hash = shadow::hash64_t{}( name );                             // accepts any range that have access by index\n        return compiletime_hash == runtime_hash;\n    } );\n\n    const auto\u0026 [name, address] = *it;\n    std::cout \u003c\u003c \"Found target export:\\n\" \u003c\u003c name \u003c\u003c \" : \" \u003c\u003c address \u003c\u003c \"\\n\\n\";\n\n    // \"location\" returns a DLL struct that contains this export\n    std::cout \u003c\u003c \"DLL that contains Sleep export is: \" \u003c\u003c shadow::dll_export( \"Sleep\" ).location().name().to_path() \u003c\u003c \"\\n\\n\";\n\n    // shared_data parses KUSER_SHARED_DATA\n    // The class is a high-level wrapper for parsing,\n    // which will save you from direct work with raw addresses\n\n    auto shared = shadow::shared_data();\n\n    std::cout \u003c\u003c shared.safe_boot_enabled() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.boot_id() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.physical_pages_num() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.kernel_debugger_present() \u003c\u003c '\\n';\n    std::wcout \u003c\u003c shared.system_root().to_path() \u003c\u003c '\\n';\n\n    std::cout \u003c\u003c shared.system().is_windows_11() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.system().is_windows_10() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.system().is_windows_7() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.system().build_number() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.system().formatted() \u003c\u003c '\\n';\n\n    std::cout \u003c\u003c shared.unix_epoch_timestamp().utc().time_since_epoch() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.unix_epoch_timestamp().utc().format_iso8601() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.unix_epoch_timestamp().local().time_since_epoch() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.unix_epoch_timestamp().local().format_iso8601() \u003c\u003c '\\n';\n    std::cout \u003c\u003c shared.timezone_offset\u003cstd::chrono::seconds\u003e() \u003c\u003c \"\\n\\n\";\n\n    // Iterators are compatible with the ranges library\n    static_assert( std::bidirectional_iterator\u003cshadow::detail::export_enumerator::iterator\u003e );\n    static_assert( std::bidirectional_iterator\u003cshadow::detail::module_enumerator::iterator\u003e );\n}\n```\n\n## Hardware processor parser\n```cpp\n#include \u003ciomanip\u003e\n#include \u003ciostream\u003e\n#include \"shadowsyscall.hpp\"\n\nint main() {\n    auto support_message = []( std::string_view isa_feature, bool is_supported ) {\n        constexpr int width = 12;\n        std::cout \u003c\u003c std::left \u003c\u003c std::setw( width ) \u003c\u003c isa_feature \u003c\u003c ( is_supported ? \"[+]\" : \"[-]\" ) \u003c\u003c std::endl;\n    };\n\n    std::cout \u003c\u003c shadow::cpu().vendor() \u003c\u003c std::endl;\n    std::cout \u003c\u003c shadow::cpu().brand() \u003c\u003c std::endl;\n\n    const auto\u0026 caches = shadow::cpu().caches();\n\n    // CPU caches parsing is supported for current processor\n    if ( caches ) {\n        std::cout \u003c\u003c \"L1 cache size:     \" \u003c\u003c caches-\u003el1_size() \u003c\u003c \"\\n\";\n        std::cout \u003c\u003c \"L2 cache size:     \" \u003c\u003c caches-\u003el2_size() \u003c\u003c \"\\n\";\n        std::cout \u003c\u003c \"L3 cache size:     \" \u003c\u003c caches-\u003el3_size() \u003c\u003c \"\\n\";\n        std::cout \u003c\u003c \"Total caches size: \" \u003c\u003c caches-\u003etotal_size().as_bytes() \u003c\u003c \"\\n\";\n    } else {\n        // Otherwise - the library does not yet support parsing for the existing processor\n        std::cout \u003c\u003c \"Cache parsing is not supported by `shadow` on your processor architecture\\n\";\n    }\n\n    support_message( \"IS_INTEL\", shadow::cpu().is_intel() );\n    support_message( \"IS_AMD\", shadow::cpu().is_amd() );\n    support_message( \"ABM\", shadow::cpu().supports_abm() );\n    support_message( \"ADX\", shadow::cpu().supports_adx() );\n    support_message( \"AES\", shadow::cpu().supports_aes() );\n    support_message( \"AVX\", shadow::cpu().supports_avx() );\n    support_message( \"AVX2\", shadow::cpu().supports_avx2() );\n    support_message( \"AVX512CD\", shadow::cpu().supports_avx512cd() );\n    support_message( \"AVX512ER\", shadow::cpu().supports_avx512er() );\n    support_message( \"AVX512F\", shadow::cpu().supports_avx512f() );\n    support_message( \"AVX512PF\", shadow::cpu().supports_avx512pf() );\n    support_message( \"BMI1\", shadow::cpu().supports_bmi1() );\n    support_message( \"BMI2\", shadow::cpu().supports_bmi2() );\n    support_message( \"CLFLUSH\", shadow::cpu().supports_clflush() );\n    support_message( \"CMPXCHG16B\", shadow::cpu().supports_cmpxchg16b() );\n    support_message( \"CX8\", shadow::cpu().supports_cx8() );\n    support_message( \"ERMS\", shadow::cpu().supports_erms() );\n    support_message( \"F16C\", shadow::cpu().supports_f16c() );\n    support_message( \"FMA\", shadow::cpu().supports_fma() );\n    support_message( \"FSGSBASE\", shadow::cpu().supports_fsgsbase() );\n    support_message( \"FXSR\", shadow::cpu().supports_fxsr() );\n    support_message( \"HLE\", shadow::cpu().supports_hle() );\n    support_message( \"INVPCID\", shadow::cpu().supports_invpcid() );\n    support_message( \"LAHF\", shadow::cpu().supports_lahf() );\n    support_message( \"LZCNT\", shadow::cpu().supports_lzcnt() );\n    support_message( \"MMX\", shadow::cpu().supports_mmx() );\n    support_message( \"MMXEXT\", shadow::cpu().supports_mmxext() );\n    support_message( \"MONITOR\", shadow::cpu().supports_monitor() );\n    support_message( \"MOVBE\", shadow::cpu().supports_movbe() );\n    support_message( \"MSR\", shadow::cpu().supports_msr() );\n    support_message( \"OSXSAVE\", shadow::cpu().supports_osxsave() );\n    support_message( \"PCLMULQDQ\", shadow::cpu().supports_pclmulqdq() );\n    support_message( \"POPCNT\", shadow::cpu().supports_popcnt() );\n    support_message( \"PREFETCHWT1\", shadow::cpu().supports_prefetchwt1() );\n    support_message( \"RDRAND\", shadow::cpu().supports_rdrand() );\n    support_message( \"RDSEED\", shadow::cpu().supports_rdseed() );\n    support_message( \"RDTSCP\", shadow::cpu().supports_rdtscp() );\n    support_message( \"RTM\", shadow::cpu().supports_rtm() );\n    support_message( \"SEP\", shadow::cpu().supports_sep() );\n    support_message( \"SHA\", shadow::cpu().supports_sha() );\n    support_message( \"SSE\", shadow::cpu().supports_sse() );\n    support_message( \"SSE2\", shadow::cpu().supports_sse2() );\n    support_message( \"SSE3\", shadow::cpu().supports_sse3() );\n    support_message( \"SSE4.1\", shadow::cpu().supports_sse4_1() );\n    support_message( \"SSE4.2\", shadow::cpu().supports_sse4_2() );\n    support_message( \"SSE4a\", shadow::cpu().supports_sse4a() );\n    support_message( \"SSSE3\", shadow::cpu().supports_ssse3() );\n    support_message( \"SYSCALL\", shadow::cpu().supports_syscall() );\n    support_message( \"TBM\", shadow::cpu().supports_tbm() );\n    support_message( \"XOP\", shadow::cpu().supports_xop() );\n    support_message( \"XSAVE\", shadow::cpu().supports_xsave() );\n}\n```\n\n## 🚀 Features\n\n- Caching each call (it is possible to disable caching)\n- Enumerate every DLL loaded to current process\n- Compute checksum of the DLL section (any) in runtime\n- Find exactly known DLL loaded to current process\n- Enumerate EAT of module\n- Resolve PE-headers and directories of module\n- Compile-time string hasher\n- Hash seed is pseudo-randomized, based on header file location\n- Syscall executor\n- Overriding syscall SSN parser\n- Execute any export at runtime\n- Doesn't leave any imports in the executable\n- CPU instruction-set support checker \u0026 cache parser\n\n## 📜 What is a syscall in Windows?\n![syscalls](https://github.com/user-attachments/assets/1719c073-669b-4e6b-b2ec-23850ba91dbc)\n\n## Thanks to\ninvers1on :heart:\n\nhttps://github.com/can1357/linux-pe\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fannihilatorq%2Fshadow_syscall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fannihilatorq%2Fshadow_syscall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fannihilatorq%2Fshadow_syscall/lists"}