Link to home
Start Free TrialLog in
Avatar of zgrp
zgrp

asked on

How to detect NTFS ADS files in C.

Hello,

I want a source code example with comments that explain how to detect if my partition is NTFS and if yes, how to scan the entire disc to search ADS (Alternate Data Stream) files.

ps: Good algorithms to make the disc search for ADS is welcome.

ps2: Is possible in C delete (not clean the content of the ADS) a NTFS file ?

Thank you.

Regards,
Avatar of zgrp
zgrp

ASKER

Increased to 300.
Avatar of jkr
Checking for NTFS is quite easy, just call 'GetVolumeInformation()' for the volume in question.

To dump all the stream information in a file, use http://www.ntsecurity.net/Files/15/16189/Listing_01.txt

#define UNICODE
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <string>
using namespace std;

void DumpStreamId( BYTE* pBuf, DWORD read )
{
      WIN32_STREAM_ID* pStreamId = ( WIN32_STREAM_ID* )pBuf;

      // always check inputs
      if( pStreamId == NULL )
      {
            assert( false );
            return;
      }

      // there can be multiple streams in a read
      // determining the offset is a little odd
      // if the stream isn't named, then the size of the
      // WIN32_STREAM_ID struct that precedes the data is
      // actually sizeof( WIN32_STREAM_ID ) - sizeof( WCHAR* )
      //
      // this means that the number of bytes that a particular stream contains is:
      // bytes = sizeof( WIN32_STREAM_ID ) + dwStreamNameSize
      //         - sizeof( WCHAR* ) + Size
      // also note that Size is a large integer

      while( (BYTE* )pStreamId - pBuf < read )
      {
            // print the name of the stream
            // this is trickier than it looks - use a wstring to get around
            // the fact that the name isn't null terminated
            if( pStreamId->dwStreamNameSize > 0 )
            {
                  wstring name;

                  // remember that dwStreamNameSize is in BYTEs,
                  // not characters
                  name.append( pStreamId->cStreamName,
                                    pStreamId->dwStreamNameSize/sizeof( WCHAR ));

                  wprintf( L"Stream Name = %s\n", name.c_str( ) );
            }

            // convert the stream ID number to text
            switch( pStreamId->dwStreamId )
            {
            case BACKUP_DATA:
                  wprintf( L"Standard data\n" );
                  break;
            case BACKUP_EA_DATA:
                  wprintf( L"Extended attribute data\n" );
                  break;
            case BACKUP_SECURITY_DATA:
                  wprintf( L"Security descriptor data\n" );
                  break;
            case BACKUP_ALTERNATE_DATA:
                  wprintf( L"Alternative data stream\n" );
                  break;
            case BACKUP_LINK:
                  wprintf( L"Hard link information\n" );
                  break;
            case BACKUP_PROPERTY_DATA:
                  wprintf( L"Property data\n" );
                  break;
            case BACKUP_OBJECT_ID:
                  wprintf( L"Objects identifiers\n" );
                  break;
            case BACKUP_REPARSE_DATA:
                  wprintf( L"Reparse points\n" );
                  break;
            case BACKUP_SPARSE_BLOCK:
                  wprintf( L"Sparse file\n" );
                  break;
            default:
                  wprintf( L"Unknown stream ID %d\n", pStreamId->dwStreamId );
                  break;
            }

            // more than one flag could be set here - have to AND it
            if( pStreamId->dwStreamAttributes & STREAM_MODIFIED_WHEN_READ )
                  wprintf( L"Stream modified when read\n" );
            
            if( pStreamId->dwStreamAttributes & STREAM_CONTAINS_SECURITY )
                  wprintf( L"Stream contains security information\n" );

            wprintf( L"Stream size: High DWORD = %d, Low DWORD = %d\n",
                        pStreamId->Size.HighPart, pStreamId->Size.LowPart );

            //figure out where the next stream is -
            if( pStreamId->Size.HighPart != 0 )
            {
                  // this is a very big stream ( > 4GB ) - bail out
                  return;
            }

            pStreamId = ( WIN32_STREAM_ID * )( (BYTE* )pStreamId +
                              sizeof( WIN32_STREAM_ID ) - sizeof( WCHAR* ) +
                              pStreamId->dwStreamNameSize + // offset for name
                              pStreamId->Size.LowPart ); // offset for data

            printf( "\n" );
      }

}

bool EnableBackupRights( void )
{
      HANDLE hToken;
      TOKEN_PRIVILEGES token_priv;

      //open current process to adjust privileges
      if( !OpenProcessToken( GetCurrentProcess( ),
                                     TOKEN_ADJUST_PRIVILEGES,
                                     &hToken ))
      {
            printf( "Cannot open process token - err = %d\n", GetLastError( ) );
            return false;
      }

      //let's build the token privilege struct -
      //first, look up the LUID for the backup privilege

      if( !LookupPrivilegeValue( NULL, //this system
                                          SE_BACKUP_NAME, //the name of the privilege
                                          &( token_priv.Privileges[0].Luid )) ) //result
      {
            printf( "Cannot lookup backup privilege - err = %d\n", GetLastError( ) );
            return false;
      }

      token_priv.PrivilegeCount = 1;
      token_priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

      // now set the privilege
      // because we're going to exit right after dumping the streams, there isn't
      // any need to save current state

      if( !AdjustTokenPrivileges( hToken, //our process token
                                            false,  //we're not disabling everything
                                            &token_priv, //address of structure
                                            sizeof( token_priv ), //size of structure
                                            NULL, NULL )) //don't save current state
      {
            //this function is a little tricky - if we were adjusting
            //more than one privilege, it could return success but not
            //adjust them all - in the general case, you need to trap this
            printf( "Could not enable backup privileges - err = %d\n", GetLastError( ) );
            return false;

      }
      else
      {
            return true;
      }
}

// this application reads a file and reports all data streams present
int wmain( int argc, WCHAR* argv[] )
{
      HANDLE hFile;

      // allocate enough space for the stream ID struct, plus
      // enough to hold the name of the stream and some data
      BYTE Buf[sizeof( WIN32_STREAM_ID ) + 1024];
      DWORD bufsize = sizeof( Buf );
      DWORD read;
      void* context;

      if( argc != 2 )
      {
            wprintf( L"Usage is %s [file name]\n", argv[0] );
            wprintf( L"Note that this also works on directories\n" );
            return -1;
      }

      // prints errors in function
      if( !EnableBackupRights( ) )
            return -1;

      hFile = CreateFile( argv[1],         // filename
                           GENERIC_READ,    // access level
                                 FILE_SHARE_READ, // share mode
                                 NULL,            // security attirubtes not needed
                                 OPEN_EXISTING,   // don't create a new file
                                 FILE_FLAG_BACKUP_SEMANTICS,
                                                  // if this flag isn't set we
                                                            // only read standard data
                                 NULL );           // no template file

      if( hFile == INVALID_HANDLE_VALUE )
      {
            wprintf( L"Cannot open %s - error = %d\n", argv[1], GetLastError( ) );
            return -1;
      }

      // now it is time to read the streams
      wprintf( L"Stream information on %s:\n", argv[1] );

      // always initialize the context to NULL
      context = NULL;

      while( 1 )
      {
            if( !BackupRead( hFile,        // file handle
                                 Buf,          // buffer to write the data
                                 sizeof( Buf ),  // size of the buffer
                                 &read,        // return value for bytes read
                                 FALSE,        // used to clear the data
                                                       // pointed to by context
                                 TRUE,        // back up the security descriptor
                                 &context ))
            {
                  wprintf( L"BackupRead failed - error = %d\n", GetLastError( ) );
                  break;
            }
            else
            {
                  if( read == 0 )
                  {
                        //end of file condition
                        break;
                  }

                  //dump this stream buffer
                  DumpStreamId( Buf, read );

                  if( read == bufsize )
                  {
                        // now seek to the next stream
                        DWORD lowseek, highseek;

                        if( !BackupSeek( hFile,
                                             ~0,
                                             ~0,
                                             &lowseek,
                                             &highseek,
                                             &context ))
                        {
                              DWORD err = GetLastError( );
                              
                              if( err == ERROR_INVALID_HANDLE )
                              {
                                    // end of file
                                    break;
                                    
                              }
                              else if( err == ERROR_SEEK )
                              {
                                    continue;
                              }
                              else
                              {
                                    wprintf( L"Error seeking to end of stream err = %d\n",
                                                err );
                                    break;
                              }
                        }
                  }
            }
      }

      // call this once more to clear the context structures
      if( !BackupRead( hFile,        // file handle
                           NULL,         // buffer to write the data
                           0,            // size of the buffer
                           &read,        // return value for bytes read
                           TRUE,         // used to clear the data
                                                 // pointed to by context
                           FALSE,        // back up the security descriptor
                           &context ))

      CloseHandle( hFile );

      return 0;
}
Avatar of zgrp

ASKER

Hello,

Thank you for all assistence.

I found a intersting article explain how to code it too, that can be useful to understand the code sent from jkr.

http://www.codeproject.com/w2k/AlternateDataStream.asp

I will test it, if work I will PAQ the thread.

Thank you,

Regards,
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of zgrp

ASKER

Thank you, perfect it is what I were looking. ;)

Closing this thread. I also want to invite you specialists at my thread opened withput replys in the programming area.

- Suggestion about anti-reversing (https://www.experts-exchange.com/questions/21833007/Anti-reverse-engineering-Win.html). *Low priority*

- Get CD information in Java (https://www.experts-exchange.com/questions/21832998/Get-CDROM-metric-information-with-Java.html)  *high priority*

- Example of a on access scanner using DDK (https://www.experts-exchange.com/questions/21832990/On-Access-Scanner-example-Kernel-Level-Win32.html) *high priority*

- Access registry and HardDrive in raw mode (open device and registry file and parse it) https://www.experts-exchange.com/questions/21833012/Dump-Registry-and-Files-in-low-level.html *Urgent priority*

Thank you.