//////////////////////////////////////////////////////////////////////////////// /// /// native_nt_permanent_section.cpp /// ------------------------------- /// /// Copyright (c) Domagoj Saric 2010 /// /// Use, modification and distribution is subject to the Boost Software /// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at /// http://www.boost.org/LICENSE_1_0.txt) /// /// For more information, see http://www.boost.org /// //////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------ #ifdef _WIN32_WINNT #if _WIN32_WINNT < 0x0500 #error target Windows version too low #endif #else // _WIN32_WINNT #define _WIN32_WINNT 0x0500 #endif // _WIN32_WINNT #include "boost/assert.hpp" #include "boost/noncopyable.hpp" #include "boost/optional.hpp" #include "boost/static_assert.hpp" #include "accctrl.h" #include "aclapi.h" #include "windows.h" // Make sure to use the latest Platform SDK (the one from VS2008, 6.0, misses // __stdcall qualifiers)... #include "winternl.h" #ifndef STATUS_SUCCESS #define STATUS_SUCCESS 0x0 #endif // STATUS_SUCCESS #ifndef STATUS_INFO_LENGTH_MISMATCH #define STATUS_INFO_LENGTH_MISMATCH 0xC0000004L #endif // STATUS_INFO_LENGTH_MISMATCH #ifndef STATUS_ACCESS_DENIED #define STATUS_ACCESS_DENIED 0xC0000022L #endif // STATUS_ACCESS_DENIED #ifndef STATUS_PRIVILEGE_NOT_HELD #define STATUS_PRIVILEGE_NOT_HELD 0xC0000061L #endif // STATUS_PRIVILEGE_NOT_HELD #ifndef STATUS_BUFFER_TOO_SMALL #define STATUS_BUFFER_TOO_SMALL 0xC0000023L #endif // STATUS_BUFFER_TOO_SMALL #ifndef STATUS_OBJECT_NAME_EXISTS #define STATUS_OBJECT_NAME_EXISTS 0x40000000L #endif // STATUS_OBJECT_NAME_EXISTS #include //------------------------------------------------------------------------------ struct CLIENT_ID { UINT_PTR UniqueProcess; UINT_PTR UniqueThread; }; typedef CLIENT_ID * PCLIENT_ID; //------------------------------------------------------------------------------ namespace boost { //------------------------------------------------------------------------------ namespace detail { //------------------------------------------------------------------------------ void * get_nt_proc( char const * const proc_name ) { return ::GetProcAddress( ::GetModuleHandleA( "ntdll.dll" ), proc_name ); } template Proc * get_nt_proc( char const * const proc_name ) { return reinterpret_cast( get_nt_proc( proc_name ) ); } typedef NTSYSAPI NTSTATUS (NTAPI NtClose_t)(IN HANDLE Handle); typedef NTSYSAPI NTSTATUS (NTAPI NtAllocateLocallyUniqueId_t)( OUT PLUID LocallyUniqueId ); typedef NTSYSAPI NTSTATUS (NTAPI NtCreateSection)( OUT PHANDLE SectionHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize OPTIONAL, IN ULONG PageAttributess, IN ULONG SectionAttributes, IN HANDLE FileHandle OPTIONAL ); typedef NTSYSAPI NTSTATUS (NTAPI NtMakeTemporaryObject)( IN HANDLE ObjectHandle ); typedef NTSYSAPI NTSTATUS (NTAPI NtCreateToken_t)( OUT PHANDLE TokenHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN TOKEN_TYPE TokenType, IN PLUID AuthenticationId, IN PLARGE_INTEGER ExpirationTime, IN PTOKEN_USER TokenUser, IN PTOKEN_GROUPS TokenGroups, IN PTOKEN_PRIVILEGES TokenPrivileges, IN PTOKEN_OWNER TokenOwner, IN PTOKEN_PRIMARY_GROUP TokenPrimaryGroup, IN PTOKEN_DEFAULT_DACL TokenDefaultDacl, IN PTOKEN_SOURCE TokenSource ); typedef NTSYSAPI NTSTATUS (NTAPI NtOpenProcessToken_t)( IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, OUT PHANDLE TokenHandle ); typedef NTSYSAPI NTSTATUS (NTAPI NtOpenProcess_t)( OUT PHANDLE ProcessHandle, IN ACCESS_MASK AccessMask, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId ); typedef NTSYSAPI NTSTATUS (NTAPI NtQuerySecurityObject_t)( IN HANDLE ObjectHandle, IN SECURITY_INFORMATION SecurityInformationClass, OUT PSECURITY_DESCRIPTOR DescriptorBuffer, IN ULONG DescriptorBufferLength, OUT PULONG RequiredLength ); typedef NTSYSAPI NTSTATUS (NTAPI NtSetSecurityObject_t)( IN HANDLE ObjectHandle, IN SECURITY_INFORMATION SecurityInformationClass, IN PSECURITY_DESCRIPTOR DescriptorBuffer ); typedef NTSYSAPI NTSTATUS (NTAPI NtQuerySystemInformation_t) ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL ); template class handle_guard : noncopyable { public: typedef RawHandle raw_handle_t; public: explicit handle_guard( raw_handle_t const handle = 0 ) : handle_( handle ) {} ~handle_guard() { close(); } void close() { BOOST_VERIFY( ( CloseHandleFunction( handle_ ) == successValue ) || !handle_ ); handle_ = raw_handle_t( 0 ); } raw_handle_t operator*( ) const { BOOST_ASSERT( handle_ ); return handle_; } raw_handle_t * operator&( ) { BOOST_ASSERT( !handle_ ); return &handle_; } handle_guard & operator=( RawHandle const handle ) { BOOST_ASSERT( !handle_ ); handle_ = handle; return *this; } bool is_null() const { return handle_ == 0; } private: raw_handle_t handle_; }; typedef handle_guard win32_handle; typedef handle_guard nt_handle ; DWORD const impersionation_required_access( TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY ); bool enable_privilege( char const * const privilege_name ) { TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; BOOST_VERIFY( ::LookupPrivilegeValueA( NULL, privilege_name, &tp.Privileges[ 0 ].Luid ) ); tp.Privileges[ 0 ].Attributes = SE_PRIVILEGE_ENABLED; nt_handle current_process_token; NTSTATUS const status( get_nt_proc( "NtOpenProcessToken" )( HANDLE( -1 ), TOKEN_ALL_ACCESS, ¤t_process_token ) ); BOOST_VERIFY( status == STATUS_SUCCESS ); bool const succeeded ( ::AdjustTokenPrivileges( *current_process_token, false, &tp, sizeof( tp ), NULL, NULL ) && ( ::GetLastError() != ERROR_NOT_ALL_ASSIGNED ) ); BOOST_ASSERT( ( ::GetLastError() != ERROR_NOT_ALL_ASSIGNED ) && "The token does not have the specified privilege." ); return succeeded; } optional build_administrators_explicit_access() { SID_IDENTIFIER_AUTHORITY /*const*/ system_sid_authority = SECURITY_NT_AUTHORITY; SID * p_sid; // Create "Administrators" SID BOOL const result ( ::AllocateAndInitializeSid ( &system_sid_authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &reinterpret_cast( p_sid ) ) ); if ( result ) { EXPLICIT_ACCESS explicit_access; explicit_access.grfAccessMode = GRANT_ACCESS; explicit_access.grfAccessPermissions = impersionation_required_access; explicit_access.grfInheritance = 0; // Build the Trustee with the SID ::BuildTrusteeWithSid( &explicit_access.Trustee, p_sid ); return explicit_access; } return none; } HANDLE get_local_system_token_2kxp() { NTSTATUS status; // See the SuperUsr example in the 2k3 PSDK... OBJECT_ATTRIBUTES poa; InitializeObjectAttributes( &poa, 0, 0, NULL, NULL ); CLIENT_ID /*const*/ local_system_pid = { 4, 0 }; nt_handle system_process; status = get_nt_proc( "NtOpenProcess" )( &system_process, PROCESS_ALL_ACCESS, &poa, &local_system_pid ); if ( status == STATUS_ACCESS_DENIED ) return NULL; BOOST_ASSERT( status == STATUS_SUCCESS ); nt_handle system_token; status = get_nt_proc( "NtOpenProcessToken" )( *system_process, READ_CONTROL | WRITE_DAC, &system_token ); BOOST_ASSERT( status == STATUS_SUCCESS ); DWORD required_size; status = get_nt_proc( "NtQuerySecurityObject" )( *system_token, DACL_SECURITY_INFORMATION, NULL, 0, &required_size ); BOOST_ASSERT( status == STATUS_BUFFER_TOO_SMALL ); SECURITY_DESCRIPTOR * const p_sd( static_cast( ::_alloca( required_size ) ) ); status = get_nt_proc( "NtQuerySecurityObject" )( *system_token, DACL_SECURITY_INFORMATION, p_sd, required_size, &required_size ); BOOST_ASSERT( status == STATUS_SUCCESS ); BOOL dacl_present; BOOL dacl_defaulted; ACL * p_acl; BOOST_VERIFY( ::GetSecurityDescriptorDacl( p_sd, &dacl_present, &p_acl, &dacl_defaulted ) ); optional /*const*/ admin_access( build_administrators_explicit_access() ); ACL * p_new_acl( NULL ); status = ::SetEntriesInAcl( 1, admin_access.get_ptr(), p_acl, &p_new_acl ); BOOST_ASSERT( status == ERROR_SUCCESS ); BOOL result; DWORD abs_sd_length( 0 ), acl_size( 0 ), dummy_size( 0 ); result = ::MakeAbsoluteSD ( p_sd, NULL, &abs_sd_length, NULL, &acl_size , NULL, &dummy_size , NULL, &dummy_size , NULL, &dummy_size ); BOOST_ASSERT( ( result == false ) && ( ::GetLastError() == ERROR_INSUFFICIENT_BUFFER ) ); SECURITY_DESCRIPTOR * const p_abs_sd( static_cast( ::_alloca( abs_sd_length ) ) ); ACL * const p_dacl ( static_cast( ::_alloca( acl_size ) ) ); result = ::MakeAbsoluteSD ( p_sd, p_abs_sd, &abs_sd_length , p_dacl , &acl_size , NULL , &dummy_size, NULL , &dummy_size, NULL , &dummy_size ); BOOST_ASSERT( result ); result = ::SetSecurityDescriptorDacl ( p_abs_sd, dacl_present, p_new_acl, dacl_defaulted ); BOOST_ASSERT( result ); status = get_nt_proc( "NtSetSecurityObject" )( *system_token, DACL_SECURITY_INFORMATION, p_abs_sd ); if ( admin_access.is_initialized() ) BOOST_VERIFY( ::FreeSid( admin_access->Trustee.ptstrName ) == NULL ); BOOST_VERIFY( ::LocalFree( p_new_acl ) == NULL ); HANDLE modified_system_token; status = get_nt_proc( "NtOpenProcessToken" )( *system_process, impersionation_required_access, &modified_system_token ); BOOST_ASSERT( status == STATUS_SUCCESS ); return modified_system_token; } HANDLE get_local_system_token_vista7() { struct SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER Reserved[3]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; LONG BasePriority; DWORD_PTR ProcessId; PVOID Reserved3; ULONG HandleCount; BYTE Reserved4[4]; PVOID Reserved5[11]; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER Reserved6[6]; }; BOOST_STATIC_ASSERT( sizeof( SYSTEM_PROCESS_INFORMATION ) == sizeof( ::SYSTEM_PROCESS_INFORMATION ) ); NTSTATUS status; ULONG required_size; status = get_nt_proc( "NtQuerySystemInformation" )( SystemProcessInformation, NULL, 0, &required_size ); BOOST_ASSERT( status == STATUS_INFO_LENGTH_MISMATCH ); SYSTEM_PROCESS_INFORMATION * p_process_info( static_cast( ::alloca( required_size ) ) ); status = get_nt_proc( "NtQuerySystemInformation" )( SystemProcessInformation, p_process_info, required_size, &required_size ); BOOST_ASSERT( status == STATUS_SUCCESS ); // http://support.microsoft.com/kb/243330 // LocalSystem SID = "S-1-5-18" static SID /*const*/ local_system_sid = { 1, 1, { 0, 0, 0, 0, 0, 5 }, 18 }; OBJECT_ATTRIBUTES poa; InitializeObjectAttributes( &poa, 0, 0, NULL, NULL ); CLIENT_ID pid; pid.UniqueThread = 0; for ( ; p_process_info->NextEntryOffset != 0 ; reinterpret_cast( p_process_info ) += p_process_info->NextEntryOffset ) { nt_handle local_system_process; pid.UniqueProcess = p_process_info->ProcessId; status = get_nt_proc( "NtOpenProcess" )( &local_system_process, PROCESS_QUERY_INFORMATION, &poa, &pid ); if ( status != STATUS_SUCCESS ) continue; HANDLE system_token; status = get_nt_proc( "NtOpenProcessToken" )( *local_system_process, impersionation_required_access, &system_token ); BOOST_ASSERT( status == STATUS_SUCCESS ); TOKEN_USER token_user[ 3 ]; DWORD requiredSize; BOOST_VERIFY( ::GetTokenInformation( system_token, TokenUser, &token_user, sizeof( token_user ), &requiredSize ) ); SID const & token_sid( *static_cast( token_user->User.Sid ) ); if ( std::memcmp( &token_sid, &local_system_sid, sizeof( token_sid ) ) == 0 ) return system_token; else BOOST_VERIFY( ( ::NtClose( system_token ) == STATUS_SUCCESS ) || ( !system_token ) ); } BOOST_ASSERT( false ); return NULL; } //------------------------------------------------------------------------------ } // namespace detail detail::nt_handle create_permanent_section( wchar_t const * const name, unsigned long long /*const*/ size ) { using namespace boost::detail; BOOST_STATIC_ASSERT( sizeof( size ) == sizeof( LARGE_INTEGER ) ); NTSTATUS status; OBJECT_ATTRIBUTES section_oa; UNICODE_STRING uname; ::RtlInitUnicodeString( &uname, name ); InitializeObjectAttributes( §ion_oa, &uname, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT, NULL, NULL ); // Try right away (if we are running as a service under LocalSystem or // already have the required rights...maybe it works for administrators // straight away on W2k...). HANDLE section_handle; status = get_nt_proc( "NtCreateSection" ) ( §ion_handle, SECTION_ALL_ACCESS, §ion_oa, reinterpret_cast( &size ), PAGE_EXECUTE_READWRITE, SEC_RESERVE, NULL ); if ( ( status == STATUS_SUCCESS ) || ( status == STATUS_OBJECT_NAME_EXISTS ) ) { BOOST_ASSERT( section_handle ); return nt_handle( section_handle ); } BOOST_ASSERT( status == STATUS_PRIVILEGE_NOT_HELD ); BOOST_VERIFY( enable_privilege( SE_DEBUG_NAME ) ); #if _WIN32_WINNT < 0x0600 nt_handle local_system_token( get_local_system_token_2kxp() ); if ( local_system_token.is_null() ) #else nt_handle #endif local_system_token = get_local_system_token_vista7(); BOOST_VERIFY( ::ImpersonateLoggedOnUser( *local_system_token ) || local_system_token.is_null() ); status = get_nt_proc( "NtCreateSection" ) ( §ion_handle, SECTION_ALL_ACCESS, §ion_oa, reinterpret_cast( &size ), PAGE_EXECUTE_READWRITE, SEC_RESERVE, NULL ); BOOST_VERIFY( ::RevertToSelf() ); if ( ( status != STATUS_SUCCESS ) && ( status != STATUS_OBJECT_NAME_EXISTS ) ) { /* ... */ } // ...status = get_nt_proc( "NtMakeTemporaryObject" )( section_handle ); return nt_handle( section_handle ); } //------------------------------------------------------------------------------ } // namespace boost //------------------------------------------------------------------------------ extern "C" int main( int /*argc*/, char * * /*argv*/ ) { return boost::create_permanent_section ( L"\\BaseNamedObjects\\Native NT Boost Interprocess Test Section", 1000000 ).is_null() ? EXIT_FAILURE : EXIT_SUCCESS; } VOID NTAPI RtlInitUnicodeString( PUNICODE_STRING const DestinationString, PCWSTR const SourceString ) { DestinationString->Buffer = const_cast( SourceString ); DestinationString->MaximumLength = DestinationString->Length = static_cast( /*std*/::wcslen( SourceString ) * sizeof( WCHAR ) ); } NTSTATUS NTAPI NtClose( HANDLE const handle ) { using namespace boost::detail; return get_nt_proc( "NtClose" )( handle ); }