#define UNICODE
#include <windows.h>
#include <stdio.h>
// Types
class SEC
{
// Class to handle security attributes
public:
SEC();
~SEC();
BOOL BuildSecurityAttributes( SECURITY_ATTRIBUTES* psa );
private:
BOOL GetUserSid( PSID* ppSidUser );
BOOL allocated;
PSECURITY_DESCRIPTOR psd;
PACL pACL;
PTOKEN_USER pTokenUser;
};
// Constants
static const WCHAR* PipeName = L"AnonymousPipe";
// Functions
static void Client( const WCHAR* server );
static void Server( void );
static void WriteToPipe( HANDLE hPipe );
static void ReadFromPipe( HANDLE hPipe );
static void DisplayError( WCHAR *pszAPI);
static BOOL WINAPI ConsoleCtrlHandler( DWORD CtrlType );
static BOOL EstablishNullSession( const WCHAR* computerName );
static BOOL RegisterNullSession( const WCHAR* pipeName );
static BOOL UnregisterNullSession( const WCHAR* pipeName );
static LONG RegInsertToMultiString( HKEY hKey, const WCHAR* valueName, const WCHAR* str );
static LONG RegRemoveFromMultiString( HKEY hKey, const WCHAR* valueName, const WCHAR* str );
static BOOL FindEntryInMultiString( const WCHAR* data, const WCHAR* entry, DWORD* offset );
void
wmain( int argc, WCHAR* argv[] )
{
if( argc < 2 )
{
wprintf( L"Usage: pipe.exe <server|client [server name for client]>\n" );
exit(1);
}
if( !SetConsoleCtrlHandler( ConsoleCtrlHandler, TRUE ) )
DisplayError( L"SetConsoleCtrlHandler" );
if( wcsicmp( argv[1], L"client") == 0 )
{
WCHAR server[512];
if( argc == 3 )
{
wcscpy( server, argv[2] );
}
else
{
wcscpy( server, L"." );
}
Client( server );
}
else
if( wcsicmp( argv[1], L"server" ) == 0 )
{
Server();
}
else
{
wprintf( L"Usage: pipe.exe <server|client [server name for client]>\n" );
exit(1);
}
}
//Runs the server
void
Server( void )
{
SEC sec;
HANDLE hPipe;
BOOL isConnected;
SECURITY_ATTRIBUTES sa;
WCHAR server[512];
swprintf( server, L"\\\\.\\pipe\\%s", PipeName );
if( !RegisterNullSession( PipeName ) )
{
wprintf( L"WARNING: could not register %s for Null Session, server will not be accessible from outside domain.\n", server );
wprintf( L"WARNING: only administrators can register a Null Session Pipe.\n" );
}
sec.BuildSecurityAttributes( &sa );
wprintf( L"INFORM: to stop, press Ctrl-C or Ctrl-Break.\n" );
while( 1 )
{
hPipe = CreateNamedPipe(
server,
PIPE_ACCESS_OUTBOUND, // read-only pipe
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
sizeof(DWORD),
0,
NMPWAIT_USE_DEFAULT_WAIT,
&sa );
if( hPipe == INVALID_HANDLE_VALUE )
DisplayError( L"CreatePipe" );
// Wait for the client to connect.
wprintf( L"INFORM: listening at %s, waiting for client to connect\n", server );
isConnected = ConnectNamedPipe( hPipe, NULL ) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if( isConnected )
{
wprintf( L"INFORM: client connected.\n" );
WriteToPipe( hPipe );
}
else
// The client could not connect. Therefore, close the pipe.
CloseHandle(hPipe);
}
}
//Runs the client
void
Client( const WCHAR* server )
{
HANDLE hPipe;
WCHAR fullName[512];
BOOL triedLogon = FALSE;
swprintf( fullName, L"\\\\%s\\pipe\\%s", server, PipeName );
wprintf( L"INFORM: connecting to %s\n", fullName );
while( 1 )
{
hPipe = CreateFile(
fullName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
SECURITY_ANONYMOUS,
NULL);
if( hPipe == INVALID_HANDLE_VALUE )
{
DWORD lastError = GetLastError(); // An error ocurred, try to analyse it
if( (lastError == ERROR_LOGON_FAILURE) ||
(lastError == ERROR_ACCESS_DENIED) ||
(lastError == ERROR_INVALID_PASSWORD)) //Add other access errors here if it is required
{
if( triedLogon )
{
DisplayError( L"CreateFile" );
}
else
if( EstablishNullSession( server ) )
{
wprintf( L"INFORM: established Null session.\n" );
}
else
{
wprintf( L"WARNING: could not establish Null Session.\n" );
}
triedLogon = TRUE;
}
else
if( lastError == ERROR_PIPE_BUSY )
{
// All pipe instances are busy. Therefore, wait for 20 seconds.
wprintf( L"INFORM: pipe busy, trying for 20secs to reconnect.\n" );
if( !WaitNamedPipe( fullName, 20000) )
DisplayError( L"WaitNamedPipe" );
}
else
{
DisplayError( L"CreateFile" );
}
}
else
{
ReadFromPipe( hPipe );
}
}
}
/**
Tries to establish a NULL session to the remote computer. A NULL session allows access to NullSessionPipes of
the server.
Input parameters: computerName: name of the server to connect to
Output parameters: TRUE | FALSE
*/
BOOL
EstablishNullSession( const WCHAR* computerName )
{
NETRESOURCE nr;
WCHAR server[MAX_PATH];
DWORD ret;
if( wcscmp( computerName, L"." ) == 0 )
{
wcscpy( server, computerName );
}
else
{
wcscpy( server, L"\\\\" );
wcscat( server, computerName );
}
ZeroMemory( &nr, sizeof(nr) );
nr.dwType = RESOURCETYPE_ANY;
nr.lpRemoteName = server;
ret = WNetAddConnection2( &nr, L"", L"", 0);
if( ret != ERROR_SUCCESS )
{
DisplayError( L"WNetAddConnection2" );
return FALSE;
}
return TRUE;
}
//Called when the user presses Ctrl-C or Ctrl-Break in the console window.
BOOL WINAPI
ConsoleCtrlHandler( DWORD CtrlType )
{
// clean up the registry
UnregisterNullSession( PipeName );
return FALSE;
}
/** Writes to the pipe
Input parameters: hPipe: handle to an opened pipe
*/
void
WriteToPipe( HANDLE hPipe )
{
DWORD i = 0;
DWORD cbWrite, cbWritten;
cbWrite = sizeof(DWORD);
while( 1 )
{
if( !WriteFile( hPipe, &i, cbWrite, &cbWritten, NULL) )
{
if( cbWrite != cbWritten) break;
}
i++;
Sleep( 1000 );
}
// Flush the pipe to allow the client to read the contents of the pipe
// before disconnecting. Disconnect the pipe, and then close the
// handle to this pipe instance.
FlushFileBuffers( hPipe );
DisconnectNamedPipe( hPipe );
CloseHandle( hPipe );
wprintf( L"INFORM: client disconnected.\n" );
}
/** Reads from pipe
Input parameters: hPipe: handle to opened pipe. */
void
ReadFromPipe( HANDLE hPipe )
{
DWORD i, cbRead;
while( 1 )
{
if( !ReadFile( hPipe, &i, sizeof(DWORD), &cbRead, NULL) )
{
if( GetLastError() != ERROR_MORE_DATA )
{
CloseHandle( hPipe );
DisplayError( L"ReadFile" );
}
}
wprintf( L" %li ", i );
};
}
/** Displays an error and exits the process
Input parameters: pszAPI: name of the Win32 function that returned an error. */
void
DisplayError( WCHAR* pszAPI )
{
LPVOID lpvMessageBuffer;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpvMessageBuffer, 0, NULL);
//... now display this string
wprintf( L"ERROR: API = %s.\n", pszAPI);
wprintf( L" error code = %d.\n", GetLastError());
wprintf( L" message = %s.\n", (char *)lpvMessageBuffer);
// Free the buffer allocated by the system
LocalFree( lpvMessageBuffer );
ExitProcess( GetLastError() );
}
/** Constructor */
SEC::
SEC()
{
allocated = FALSE;
psd = NULL;
pACL = NULL;
pTokenUser = NULL;
}
/** Destructor */
SEC::
~SEC()
{
if( allocated )
{
if( psd ) HeapFree( GetProcessHeap(), 0, psd );
if( pACL ) HeapFree( GetProcessHeap(), 0 , pACL );
if( pTokenUser ) HeapFree( GetProcessHeap(), 0, pTokenUser );
allocated = FALSE;
}
}
/** Builds security attributes that allows read-only access to everyone
Input parameters: psa: security attributes to build
Output parameters: TRUE | FALSE */
BOOL SEC::
BuildSecurityAttributes( SECURITY_ATTRIBUTES* psa )
{
DWORD dwAclSize;
PSID pSidAnonymous = NULL; // Well-known AnonymousLogin SID
PSID pSidOwner = NULL;
if( allocated ) return FALSE;
SID_IDENTIFIER_AUTHORITY siaAnonymous = SECURITY_NT_AUTHORITY;
SID_IDENTIFIER_AUTHORITY siaOwner = SECURITY_NT_AUTHORITY;
do
{
psd = (PSECURITY_DESCRIPTOR) HeapAlloc( GetProcessHeap(),
HEAP_ZERO_MEMORY,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if( psd == NULL )
{
DisplayError( L"HeapAlloc" );
break;
}
if( !InitializeSecurityDescriptor( psd, SECURITY_DESCRIPTOR_REVISION) )
{
DisplayError( L"InitializeSecurityDescriptor" );
break;
}
// Build anonymous SID
AllocateAndInitializeSid( &siaAnonymous, 1,
SECURITY_ANONYMOUS_LOGON_RID,
0,0,0,0,0,0,0,
&pSidAnonymous
);
if( !GetUserSid( &pSidOwner ) )
{
return FALSE;
}
// Compute size of ACL
dwAclSize = sizeof(ACL) +
2 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
GetLengthSid( pSidAnonymous ) +
GetLengthSid( pSidOwner );
pACL = (PACL)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwAclSize );
if( pACL == NULL )
{
DisplayError( L"HeapAlloc" );
break;
}
InitializeAcl( pACL, dwAclSize, ACL_REVISION);
if( !AddAccessAllowedAce( pACL,
ACL_REVISION,
GENERIC_ALL,
pSidOwner
))
{
DisplayError( L"AddAccessAllowedAce" );
break;
}
if( !AddAccessAllowedAce( pACL,
ACL_REVISION,
FILE_GENERIC_READ, //GENERIC_READ | GENERIC_WRITE,
pSidAnonymous
) )
{
DisplayError( L"AddAccessAllowedAce" );
break;
}
if( !SetSecurityDescriptorDacl( psd, TRUE, pACL, FALSE) )
{
DisplayError( L"SetSecurityDescriptorDacl" );
break;
}
psa->nLength = sizeof(SECURITY_ATTRIBUTES);
psa->bInheritHandle = TRUE;
psa->lpSecurityDescriptor = psd;
allocated = TRUE;
}while(0);
if( pSidAnonymous ) FreeSid( pSidAnonymous );
if( pSidOwner ) FreeSid( pSidOwner );
if( !allocated )
{
if( psd ) HeapFree( GetProcessHeap(), 0, psd );
if( pACL ) HeapFree( GetProcessHeap(), 0 , pACL );
}
return allocated;
}
/** Obtains the SID of the user running this thread or process.
Output parameters: ppSidUser: the SID of the current user,
TRUE | FALSE: could not obtain the user SID */
BOOL SEC::
GetUserSid( PSID* ppSidUser )
{
HANDLE hToken;
DWORD dwLength;
DWORD cbName = 250;
DWORD cbDomainName = 250;
if( !OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken) )
{
if( GetLastError() == ERROR_NO_TOKEN )
{
if( !OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken) )
{
return FALSE;
}
}
else
{
return FALSE;
}
}
if( !GetTokenInformation( hToken, // handle of the access token
TokenUser, // type of information to retrieve
pTokenUser, // address of retrieved information
0, // size of the information buffer
&dwLength // address of required buffer size
))
{
if( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
pTokenUser = (PTOKEN_USER) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength );
if( pTokenUser == NULL )
{
return FALSE;
}
}
else
{
return FALSE;
}
}
if( !GetTokenInformation( hToken, // handle of the access token
TokenUser, // type of information to retrieve
pTokenUser, // address of retrieved information
dwLength, // size of the information buffer
&dwLength // address of required buffer size
))
{
HeapFree( GetProcessHeap(), 0, pTokenUser );
pTokenUser = NULL;
return FALSE;
}
*ppSidUser = pTokenUser->User.Sid;
return TRUE;
}
/** Registers a pipe as a NULL SESSION pipe that may be accessed by unauthenticated users.
Input parameters: serverName: pipe to register
Output parameters: TRUE: pipe registered
| FALSE: could no register pipe. */
BOOL
RegisterNullSession( const WCHAR* pipeName )
{
LONG status;
HKEY hKey;
// Opens key
status = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_QUERY_VALUE | KEY_SET_VALUE,
NULL,
&hKey,
NULL);
if( status != ERROR_SUCCESS )
{
return FALSE;
}
status = RegInsertToMultiString( hKey, L"NullSessionPipes", pipeName );
RegCloseKey( hKey );
if( status != ERROR_SUCCESS )
{
return FALSE;
}
return TRUE;
}
/** Unregisters a pipe from the NULL SESSION pipe list.
Input parameters: pipeName: pipe to unregister
Output parameters: TRUE: pipe unregistered or pipe was not registered
| FALSE: could not unregister pipe
*/
BOOL
UnregisterNullSession( const WCHAR* pipeName )
{
LONG status;
HKEY hKey;
// Opens key
status = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_QUERY_VALUE | KEY_SET_VALUE,
NULL,
&hKey,
NULL);
if( status != ERROR_SUCCESS )
{
return FALSE;
}
status = RegRemoveFromMultiString( hKey, L"NullSessionPipes", pipeName );
RegCloseKey( hKey );
if( (status != ERROR_SUCCESS) && (status != ERROR_INVALID_NAME) )
{
return FALSE;
}
return TRUE;
}
/** Inserts a string to a MULTI_SZ value. If the string is already present, nothing will be added. This
avoids duplicated strings.
Input parameters: hKey: handle to open key,
valueName: name of the multistring value to which the str value will be appended,
str: string to append
<-- ERROR_SUCCESS
| any error code returned by RegQueryValueEx and RegSetValueEx */
LONG
RegInsertToMultiString( HKEY hKey, const WCHAR* valueName, const WCHAR* str )
{
LONG status;
BYTE* data;
DWORD newSize, oldSize, strSize;
// Obtains the current data size
status = RegQueryValueEx( hKey,
valueName,
NULL,
NULL,
NULL,
&oldSize );
if( status != ERROR_SUCCESS )
{
return status;
}
// Allocates memory to hold all the data
strSize = (wcslen(str) + 1) * sizeof(WCHAR);
newSize = oldSize + strSize;
data = (BYTE*)HeapAlloc( GetProcessHeap(), 0 , newSize );
if( data == NULL )
{
return ERROR_OUTOFMEMORY;
}
// Obtains the current data
status = RegQueryValueEx( hKey,
valueName,
NULL,
NULL,
data,
&oldSize );
if( status != ERROR_SUCCESS )
{
HeapFree( GetProcessHeap(), 0 , data );
return status;
}
// Looks if the data is already there
if( FindEntryInMultiString( (WCHAR*)data, str, NULL ) )
{
HeapFree( GetProcessHeap(), 0 , data );
return ERROR_SUCCESS;
}
// Appends our entry
CopyMemory( data+oldSize-2, str, strSize );
// Append another NULL terminator
data[newSize-2] = 0;
data[newSize-1] = 0;
// Sets the new data
status = RegSetValueEx( hKey,
valueName,
0,
REG_MULTI_SZ,
data,
newSize );
HeapFree( GetProcessHeap(), 0 , data );
return status;
}
/** Removes a string from a MULTI_SZ value.
Input parameters: hKey: handle to open key,
valueName: name of the multistring value to which the str value will be appended,
str: string to append
<-- ERROR_SUCCESS
| ERROR_INVALID_NAME: str was not found
| any error code returned by RegQueryValueEx and RegSetValueEx */
LONG
RegRemoveFromMultiString( HKEY hKey, const WCHAR* valueName, const WCHAR* str )
{
LONG status;
BYTE* data;
DWORD size, offset, strSize;
BYTE* p;
BOOL found = FALSE;
// Obtains the current data size
status = RegQueryValueEx( hKey,
valueName,
NULL,
NULL,
NULL,
&size );
if( status != ERROR_SUCCESS )
{
return status;
}
// Allocates memory to hold all the data
strSize = (wcslen(str) + 1) * sizeof(WCHAR);
data = (BYTE*)HeapAlloc( GetProcessHeap(), 0 , size );
if( data == NULL )
{
return ERROR_OUTOFMEMORY;
}
// Obtains the current data
status = RegQueryValueEx( hKey,
valueName,
NULL,
NULL,
data,
&size );
if( status != ERROR_SUCCESS )
{
HeapFree( GetProcessHeap(), 0 , data );
return status;
}
// Searches the entry to be removed
found = FindEntryInMultiString( (WCHAR*)data, str, &offset );
if( !found )
{
HeapFree( GetProcessHeap(), 0 , data );
return ERROR_INVALID_NAME;
}
// Writes over the entry, and then puts it back in the registry
p = data + offset;
CopyMemory( p, p + strSize, size - strSize - offset );
status = RegSetValueEx( hKey,
valueName,
0,
REG_MULTI_SZ,
data,
size - strSize );
HeapFree( GetProcessHeap(), 0 , data );
return status;
}
/** Input parameters: data: multi string in which the entry is to be found,
entry: to find
offset: if not NULL holds the number of bytes from the beginning of data where entry starts
Output parameters: TRUE: found entry
| FALSE: did not find entry */
BOOL
FindEntryInMultiString( const WCHAR* data, const WCHAR* entry, DWORD* offset )
{
BYTE* p;
DWORD read, tmp;
BOOL found = FALSE;
p = (BYTE*)data;
read = 0;
while( !( (*p == 0 ) && ((*(p+1)) == 0 ) ) )
{
if( wcscmp( (WCHAR*)p, entry ) == 0 )
{
found = TRUE;
break;
}
tmp = (wcslen( (WCHAR*)p ) + 1) * sizeof(WCHAR);
read += tmp ;
p += tmp;
}
if( offset )
{
if( found )
{
*offset = read;
}
else
{
*offset = 0;
}
}
return found;
}